1.1 --- a/micropython/__init__.py Tue Nov 06 00:12:46 2012 +0100
1.2 +++ b/micropython/__init__.py Tue Nov 06 00:16:26 2012 +0100
1.3 @@ -39,6 +39,7 @@
1.4
1.5 from micropython.data import *
1.6 from micropython.errors import *
1.7 +from micropython.objectset import ObjectSet
1.8 from micropython.program import Location
1.9 from micropython.types import *
1.10 import micropython.ast
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/micropython/branch.py Tue Nov 06 00:16:26 2012 +0100
2.3 @@ -0,0 +1,762 @@
2.4 +#!/usr/bin/env python
2.5 +
2.6 +"""
2.7 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
2.8 +
2.9 +This program is free software; you can redistribute it and/or modify it under
2.10 +the terms of the GNU General Public License as published by the Free Software
2.11 +Foundation; either version 3 of the License, or (at your option) any later
2.12 +version.
2.13 +
2.14 +This program is distributed in the hope that it will be useful, but WITHOUT
2.15 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
2.16 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
2.17 +details.
2.18 +
2.19 +You should have received a copy of the GNU General Public License along with
2.20 +this program. If not, see <http://www.gnu.org/licenses/>.
2.21 +"""
2.22 +
2.23 +from micropython.errors import *
2.24 +from micropython.objectset import *
2.25 +from micropython.types import *
2.26 +
2.27 +try:
2.28 + set
2.29 +except NameError:
2.30 + from sets import Set as set
2.31 +
2.32 +class AttributeUsage:
2.33 +
2.34 + "Management of attribute usage in a program unit."
2.35 +
2.36 + def __init__(self):
2.37 +
2.38 + # Attributes accessed on objects, potentially narrowing their types.
2.39 + # Specific namespaces should define known names during initialisation.
2.40 + # For example, functions can define names belonging to parameters.
2.41 +
2.42 + # Attribute users, defining names which use attributes.
2.43 +
2.44 + self.attribute_users = [{}] # stack of assignments and branches
2.45 +
2.46 + # Define attribute usage to identify active program sections.
2.47 + # Attribute users are AST nodes defining names.
2.48 +
2.49 + self.all_attribute_users = set()
2.50 +
2.51 + # Finalisation of a program unit's information.
2.52 +
2.53 + def finalise_attribute_usage(self):
2.54 +
2.55 + "Propagate attribute usage for the namespace to the importer."
2.56 +
2.57 + module = self.module
2.58 + importer = module and module.importer
2.59 +
2.60 + if importer is not None:
2.61 +
2.62 + # Visit each user and examine the attribute usage for each name.
2.63 +
2.64 + for user in self.all_attribute_users:
2.65 +
2.66 + # First, visit the contributors and combine their attribute
2.67 + # usage with the usage recorded directly on the user.
2.68 +
2.69 + self.get_usage_from_contributors(user)
2.70 +
2.71 + # Record the defining user on each contributor.
2.72 +
2.73 + for contributor in user._attrcontributors:
2.74 + contributor._attrdefs.append(user)
2.75 +
2.76 + # Then, tell the importer about the usage.
2.77 +
2.78 + for name in user._attrnames.keys():
2.79 +
2.80 + # Only provide information about names defined by this user.
2.81 +
2.82 + usage = user._attrcombined.get(name, [])
2.83 +
2.84 + # Skip reporting where no actual usage occurs.
2.85 +
2.86 + if usage is None:
2.87 + continue
2.88 +
2.89 + # Eliminate non-usage.
2.90 +
2.91 + importer.use_names(user, name, tuple([attrnames for attrnames in usage if attrnames]), self.full_name())
2.92 +
2.93 + def finalise_users(self, objtable):
2.94 +
2.95 + "Record the object types for generating guards."
2.96 +
2.97 + # Visit each user and examine the attribute usage for each name.
2.98 +
2.99 + for user in self.all_attribute_users:
2.100 + user._attrtypes = self._deduce_types(user._attrcombined, objtable)
2.101 + self._finalise_contributor(user, objtable)
2.102 +
2.103 + def _finalise_contributors(self, node, objtable):
2.104 +
2.105 + """
2.106 + Visit the contributing branches of 'node', finalising them using the
2.107 + given 'objtable'.
2.108 + """
2.109 +
2.110 + for contributor in node._attrbranches:
2.111 + self._finalise_contributor(contributor, objtable)
2.112 +
2.113 + def _finalise_contributor(self, node, objtable):
2.114 +
2.115 + """
2.116 + Record the specific object types being used in various regions of a
2.117 + program unit.
2.118 + """
2.119 +
2.120 + if node._attrspecifictypes is None:
2.121 + merged = {}
2.122 +
2.123 + # Get the combined usage information from the user definitions.
2.124 +
2.125 + for user in node._attrdefs or [node]:
2.126 +
2.127 + # Filter the usage for each name using the local usage
2.128 + # information.
2.129 +
2.130 + for name, usage in user._attrcombined.items():
2.131 + localusage = node._attrnames.get(name)
2.132 +
2.133 + if usage and localusage:
2.134 + if not merged.has_key(name):
2.135 + merged[name] = ObjectSet()
2.136 +
2.137 + for attrnames, value in usage.items():
2.138 + if attrnames and localusage.issubset(attrnames):
2.139 + merged[name][attrnames] = value
2.140 +
2.141 + node._attrmerged = merged
2.142 + node._attrspecifictypes = self._deduce_types(node._attrmerged, objtable)
2.143 +
2.144 + self._finalise_contributors(node, objtable)
2.145 +
2.146 + def _deduce_types(self, usage, objtable):
2.147 +
2.148 + """
2.149 + Deduce the types for names from the given attribute 'usage' and using
2.150 + the given 'objtable'.
2.151 + """
2.152 +
2.153 + attrtypes = {}
2.154 + for name, combined_usage in usage.items():
2.155 + if combined_usage is not None:
2.156 + objtypes = get_object_types_for_usage(combined_usage, objtable, name, self.full_name(), True, self.module.importer)
2.157 + if objtypes:
2.158 + if self.is_method() and name == "self":
2.159 + objtypes = filter_using_self(objtypes, self.parent)
2.160 + attrtypes[name] = objtypes
2.161 + return attrtypes
2.162 +
2.163 + def get_usage_from_contributors(self, node):
2.164 +
2.165 + """
2.166 + Obtain usage information from the given 'node', combined with usage
2.167 + details from its contributors, returning a tuple containing a set of all
2.168 + contributors employed along with a dictionary mapping names to lists of
2.169 + usage possibilities (each a collection of attribute names).
2.170 + """
2.171 +
2.172 + unfinished = {}
2.173 +
2.174 + # Process any unprocessed contributors, indicating the unfinished state
2.175 + # of the associated data.
2.176 +
2.177 + if node._attrcombined is None:
2.178 + node._attrcombined = Unset
2.179 +
2.180 + for contributor in node._attrbranches:
2.181 +
2.182 + # Get contributor details.
2.183 +
2.184 + unfinished_contributors = self.get_usage_from_contributors(contributor)
2.185 +
2.186 + # Collect unfinished contributors and affected nodes.
2.187 +
2.188 + # Where the contributor is already set to Unset, a loop has
2.189 + # occurred and this node will need to have its usage
2.190 + # recalculated later for the unfinished contributor.
2.191 +
2.192 + if contributor._attrcombined is Unset:
2.193 + if not unfinished.has_key(contributor):
2.194 + unfinished[contributor] = []
2.195 + unfinished[contributor].append(node)
2.196 + continue
2.197 +
2.198 + # Where the contributor provides usage details, it may also
2.199 + # communicate unfinished contributor information. As a
2.200 + # consequence, this node is also affected.
2.201 +
2.202 + for unfinished_contributor, nodes in unfinished_contributors.items():
2.203 + if not unfinished.has_key(unfinished_contributor):
2.204 + unfinished[unfinished_contributor] = nodes
2.205 + else:
2.206 + unfinished[unfinished_contributor] += nodes
2.207 +
2.208 + if node not in unfinished[unfinished_contributor]:
2.209 + unfinished[unfinished_contributor].append(node)
2.210 +
2.211 + # Set the current state of the usage on this node.
2.212 +
2.213 + node._attrcombined, node._attrcontributors = \
2.214 + self.get_usage_from_contributors_for_node(node)
2.215 +
2.216 + # Complete unfinished contributors relying on this node.
2.217 +
2.218 + if unfinished.has_key(node):
2.219 + processed = set()
2.220 + for contributor in unfinished[node]:
2.221 + if not contributor in processed:
2.222 + processed.add(contributor)
2.223 +
2.224 + contributor._attrcombined, contributor._attrcontributors = \
2.225 + self.get_usage_from_contributors_for_node(contributor)
2.226 +
2.227 + del unfinished[node]
2.228 +
2.229 + return unfinished
2.230 +
2.231 + def get_usage_from_contributors_for_node(self, node):
2.232 +
2.233 + # Visit each contributor, gathering usage for each name.
2.234 +
2.235 + contributor_usage = {}
2.236 + all_contributions = []
2.237 + all_contributors = set()
2.238 +
2.239 + for contributor in node._attrbranches:
2.240 + usage = contributor._attrcombined
2.241 + if usage is not Unset:
2.242 + all_contributions.append(usage)
2.243 +
2.244 + all_contributors.add(contributor)
2.245 + contributors = contributor._attrcontributors
2.246 + if contributors is not None:
2.247 + all_contributors.update(contributors)
2.248 +
2.249 + # Get contributed usage for each contributor.
2.250 + # This gathers usage for each name such as {(a, b), (c, d)} and
2.251 + # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}.
2.252 +
2.253 + update_mapping_dict(contributor_usage, all_contributions)
2.254 +
2.255 + # Then get the resulting usage.
2.256 + # First, make the current usage compatible with the contributed
2.257 + # usage: this makes the attribute usage for each name merely one
2.258 + # member in a list of many possibilities.
2.259 + # Then, combine the current usage with the contributed usage.
2.260 + # Thus, usage of {(f, g)} combined with {(a, b), (c, d)} would give
2.261 + # {(f, g, a, b), (f, g, c, d)}.
2.262 +
2.263 + return combine_mapping_dicts(deepen_mapping_dict(node._attrnames), contributor_usage), all_contributors
2.264 +
2.265 + # Attribute usage methods.
2.266 +
2.267 + def use_attribute(self, name, attrname, value=None):
2.268 +
2.269 + """
2.270 + Note usage on the attribute user 'name' of the attribute 'attrname',
2.271 + noting an assignment if 'value' is specified.
2.272 + """
2.273 +
2.274 + return self._use_attribute(name, attrname, value)
2.275 +
2.276 + def use_specific_attribute(self, objname, attrname):
2.277 +
2.278 + "Declare the usage on 'objname' of the given 'attrname'."
2.279 +
2.280 + self._use_specific_attribute(objname, attrname)
2.281 +
2.282 + # These shadow various methods in the InspectedModule class, and provide
2.283 + # implementations generally.
2.284 +
2.285 + def _use_specific_attribute(self, objname, attrname, from_name=None):
2.286 +
2.287 + """
2.288 + Note attribute usage specifically on 'objname' - an object which is
2.289 + known at inspection time - or in the current unit if 'objname' is None,
2.290 + nominating a specific attribute 'attrname'.
2.291 +
2.292 + This bypasses attribute user mechanisms.
2.293 + """
2.294 +
2.295 + from_name = from_name or self.full_name()
2.296 + objname = objname or from_name
2.297 + module = self.module
2.298 + importer = module and module.importer
2.299 +
2.300 + if importer is not None:
2.301 + importer.use_specific_name(objname, attrname, from_name)
2.302 +
2.303 + def _use_attribute(self, name, attrname, value=None):
2.304 +
2.305 + """
2.306 + Indicate the use of the given 'name' in this namespace of an attribute
2.307 + with the given 'attrname'. If the optional 'value' is specified, an
2.308 + assignment using the given 'value' is recorded.
2.309 + """
2.310 +
2.311 + users = self.attribute_users[-1]
2.312 +
2.313 + # If no users are defined for the name, it cannot be handled.
2.314 +
2.315 + if not users.has_key(name):
2.316 + return []
2.317 +
2.318 + # Add the usage to all current users.
2.319 +
2.320 + for user in users[name]:
2.321 + values = user._attrnames[name]
2.322 + if values is None:
2.323 + values = user._attrnames[name] = ObjectSet()
2.324 +
2.325 + # Add an entry for the attribute, optionally with an assigned
2.326 + # value.
2.327 +
2.328 + values.add(attrname)
2.329 + if value is not None:
2.330 + values[attrname].add(value)
2.331 +
2.332 + return users[name]
2.333 +
2.334 + def _define_attribute_user(self, node):
2.335 +
2.336 + """
2.337 + Define 'node' as the user of attributes, indicating the point where the
2.338 + user is defined.
2.339 + """
2.340 +
2.341 + name = node.name
2.342 + self._define_attribute_user_for_name(node, name)
2.343 +
2.344 + def _define_attribute_user_for_name(self, node, name):
2.345 +
2.346 + "Define 'node' as the user of attributes for the given 'name'."
2.347 +
2.348 + users = self.attribute_users[-1]
2.349 +
2.350 + # This node overrides previous definitions.
2.351 +
2.352 + users[name] = set([node])
2.353 +
2.354 + # Record the attribute combinations for the name.
2.355 +
2.356 + self._init_attribute_user_for_name(node, name)
2.357 +
2.358 + # Remember this user.
2.359 +
2.360 + self.all_attribute_users.add(node)
2.361 +
2.362 + def _init_attribute_user_for_name(self, node, name):
2.363 +
2.364 + "Make sure that 'node' is initialised for 'name'."
2.365 +
2.366 + self._init_attribute_user(node)
2.367 + node._attrnames[name] = None
2.368 +
2.369 + def _init_attribute_user(self, node):
2.370 +
2.371 + # Attribute usage for names.
2.372 +
2.373 + if node._attrnames is None:
2.374 + node._attrnames = {}
2.375 + node._attrmerged = {}
2.376 +
2.377 + # Branches contributing usage to this node.
2.378 +
2.379 + if node._attrbranches is None:
2.380 + node._attrbranches = []
2.381 +
2.382 + # Definitions receiving usage from this node.
2.383 +
2.384 + if node._attrdefs is None:
2.385 + node._attrdefs = []
2.386 +
2.387 + def _define_attribute_accessor(self, name, attrname, node, value):
2.388 +
2.389 + # NOTE: Revisiting of nodes may occur for loops.
2.390 +
2.391 + if node._attrusers is None:
2.392 + node._attrusers = set()
2.393 +
2.394 + node._attrusers.update(self.use_attribute(name, attrname, value))
2.395 + node._username = name
2.396 +
2.397 +class ScopeUsage:
2.398 +
2.399 + "Scope usage tracking."
2.400 +
2.401 + def __init__(self):
2.402 +
2.403 + # Scope usage, indicating the origin of names.
2.404 +
2.405 + self.scope_usage = [{}] # stack of scope usage
2.406 +
2.407 + def define_scope(self, name, scope):
2.408 +
2.409 + """
2.410 + Define 'name' as being from the given 'scope' in the current namespace.
2.411 + """
2.412 +
2.413 + self.scope_usage[-1][name] = scope
2.414 +
2.415 + def note_scope(self, name, scope):
2.416 +
2.417 + """
2.418 + Note usage of 'name' from the given 'scope' in the current namespace.
2.419 + If a conflict has been recorded previously, raise an exception.
2.420 + """
2.421 +
2.422 + scope_usage = self.scope_usage[-1]
2.423 +
2.424 + if scope_usage.has_key(name):
2.425 + found_scope = scope_usage[name]
2.426 + if isinstance(found_scope, ScopeConflict):
2.427 + raise InspectError("Scope conflict for %r: defined as %s." % (
2.428 + name, ", ".join(found_scope.scopes)))
2.429 +
2.430 + scope_usage[name] = scope
2.431 +
2.432 + def used_in_scope(self, name, scope):
2.433 +
2.434 + """
2.435 + Return whether 'name' is used from the given 'scope' in the current
2.436 + namespace.
2.437 + """
2.438 +
2.439 + scope_usage = self.scope_usage[-1]
2.440 + return scope_usage.get(name) == scope
2.441 +
2.442 +class BranchTracking(AttributeUsage, ScopeUsage):
2.443 +
2.444 + """
2.445 + Management of branches, relying on attribute usage support.
2.446 + """
2.447 +
2.448 + def __init__(self):
2.449 + AttributeUsage.__init__(self)
2.450 + ScopeUsage.__init__(self)
2.451 +
2.452 + # Details of attribute users at each active branch level.
2.453 +
2.454 + self.attribute_user_shelves = []
2.455 +
2.456 + # Details of name scopes at each active branch level.
2.457 +
2.458 + self.scope_shelves = []
2.459 +
2.460 + # Suspended user details plus loop details.
2.461 +
2.462 + self.suspended_broken_users = [] # stack of lists of user dicts
2.463 + self.suspended_continuing_users = [] # stack of lists of user dicts
2.464 +
2.465 + # Abandoned usage, useful for reviving usage for exception handlers.
2.466 +
2.467 + self.abandoned_users = [[]] # stack of lists of users
2.468 +
2.469 + # Methods shadowing the convenience methods provided during inspection.
2.470 +
2.471 + def _new_branchpoint(self, loop_node=None):
2.472 +
2.473 + """
2.474 + Establish a new branchpoint where several control-flow branches diverge
2.475 + and subsequently converge.
2.476 + """
2.477 +
2.478 + self.attribute_user_shelves.append([])
2.479 + self.scope_shelves.append([])
2.480 +
2.481 + if loop_node is not None:
2.482 + self.suspended_broken_users.append([])
2.483 + self.suspended_continuing_users.append((loop_node, []))
2.484 +
2.485 + def _new_branch(self, node):
2.486 +
2.487 + """
2.488 + Establish a new control-flow branch, transferring attribute usage to
2.489 + the new branch so that it may be augmented for each name locally.
2.490 +
2.491 + Add the given 'node' as an active user to be informed of attribute
2.492 + usage.
2.493 + """
2.494 +
2.495 + attribute_users = self.attribute_users[-1]
2.496 +
2.497 + # Define this node as the active attribute user for all currently
2.498 + # defined names.
2.499 +
2.500 + new_users = {}
2.501 +
2.502 + for name in attribute_users.keys():
2.503 + new_users[name] = [node]
2.504 + self._init_attribute_user_for_name(node, name)
2.505 +
2.506 + self._init_attribute_user(node)
2.507 + self.attribute_users.append(new_users)
2.508 +
2.509 + # Add this user as a contributor to the previously active users.
2.510 +
2.511 + self._connect_users_to_branch(attribute_users, node)
2.512 +
2.513 + # Retain a record of scope usage.
2.514 +
2.515 + scope_usage = {}
2.516 + scope_usage.update(self.scope_usage[-1])
2.517 + self.scope_usage.append(scope_usage)
2.518 +
2.519 + # Retain a record of abandoned branch users.
2.520 +
2.521 + self.abandoned_users.append([])
2.522 +
2.523 + def _connect_users_to_branch(self, attribute_users, node):
2.524 +
2.525 + """
2.526 + Given the 'attribute_users' mapping, connect the users referenced in the
2.527 + mapping to the given branch 'node'.
2.528 + """
2.529 +
2.530 + all_users = set()
2.531 +
2.532 + for users in attribute_users.values():
2.533 + all_users.update(users)
2.534 +
2.535 + for user in all_users:
2.536 + self._init_attribute_user(user)
2.537 + user._attrbranches.append(node)
2.538 +
2.539 + def _abandon_branch(self, retain_branch=True):
2.540 +
2.541 + """
2.542 + Abandon scope usage, permitting locally different scopes for names,
2.543 + provided these cannot "escape" from the branch.
2.544 + """
2.545 +
2.546 + attribute_users = self.attribute_users[-1]
2.547 +
2.548 + self.attribute_users[-1] = {}
2.549 + self.scope_usage[-1] = abandoned_branch_scope
2.550 +
2.551 + if retain_branch:
2.552 + self.abandoned_users[-1].append(attribute_users)
2.553 +
2.554 + def _suspend_broken_branch(self):
2.555 +
2.556 + """
2.557 + Suspend a branch for resumption after the current loop.
2.558 + """
2.559 +
2.560 + attribute_users = self.attribute_users[-1]
2.561 +
2.562 + users = self.suspended_broken_users[-1]
2.563 + users.append(attribute_users)
2.564 + self._abandon_branch(False)
2.565 +
2.566 + def _suspend_continuing_branch(self):
2.567 +
2.568 + """
2.569 + Suspend a branch for resumption after the current iteration.
2.570 + """
2.571 +
2.572 + attribute_users = self.attribute_users[-1]
2.573 +
2.574 + loop_node, users = self.suspended_continuing_users[-1]
2.575 + users.append(attribute_users)
2.576 + self._abandon_branch(False)
2.577 +
2.578 + def _shelve_branch(self):
2.579 +
2.580 + """
2.581 + Shelve the current control-flow branch, recording the attribute usage
2.582 + for subsequent merging. If this branch should be abandoned, the usage
2.583 + observations are still recorded but will not contribute to subsequent
2.584 + observations after a merge.
2.585 + """
2.586 +
2.587 + users = self.attribute_users.pop()
2.588 + self.attribute_user_shelves[-1].append(users)
2.589 +
2.590 + scope_usage = self.scope_usage.pop()
2.591 + self.scope_shelves[-1].append(scope_usage)
2.592 +
2.593 + def _merge_branches(self):
2.594 +
2.595 + """
2.596 + Merge control-flow branches. This should find the users active within
2.597 + each branch, which have been "shelved", and update the active users
2.598 + dictionary with these contributions.
2.599 + """
2.600 +
2.601 + # Combine the attribute users. This ensures that a list of users
2.602 + # affected by attribute usage is maintained for the current branch.
2.603 +
2.604 + all_shelved_users = self.attribute_user_shelves.pop()
2.605 + new_users = merge_mapping_dicts(all_shelved_users)
2.606 + self.attribute_users[-1] = new_users
2.607 +
2.608 + # Abandoned users are retained for exception handling purposes.
2.609 +
2.610 + all_abandoned_users = self.abandoned_users.pop()
2.611 + new_abandoned_users = merge_mapping_dicts(all_abandoned_users)
2.612 + self.abandoned_users[-1].append(new_abandoned_users)
2.613 +
2.614 + # Combine the scope usage.
2.615 +
2.616 + scope_usage = self.scope_usage[-1]
2.617 + new_scope_usage = {}
2.618 +
2.619 + all_scope_usage = self.scope_shelves.pop()
2.620 + all_scope_names = set()
2.621 +
2.622 + # Find all the names for whom scope information has been defined.
2.623 +
2.624 + for shelved_usage in all_scope_usage:
2.625 + all_scope_names.update(shelved_usage.keys())
2.626 +
2.627 + for shelved_usage in all_scope_usage:
2.628 + for name in all_scope_names:
2.629 +
2.630 + # Find the recorded scope for the name.
2.631 +
2.632 + if shelved_usage.has_key(name):
2.633 + scope = shelved_usage[name]
2.634 + elif scope_usage.has_key(name):
2.635 + scope = scope_usage[name]
2.636 +
2.637 + # For abandoned branches, no scope is asserted for a name.
2.638 +
2.639 + elif isinstance(shelved_usage, AbandonedBranchScope):
2.640 + scope = None
2.641 +
2.642 + # If no scope is recorded, find a suitable external source.
2.643 +
2.644 + else:
2.645 + attr, scope, full_name = self._get_with_scope(name, external=1)
2.646 +
2.647 + # Attempt to record the scope, testing for conflicts.
2.648 +
2.649 + if scope:
2.650 + if not new_scope_usage.has_key(name):
2.651 + new_scope_usage[name] = scope
2.652 + else:
2.653 + new_scope = new_scope_usage[name]
2.654 + if new_scope != scope:
2.655 + if isinstance(new_scope, ScopeConflict):
2.656 + if isinstance(scope, ScopeConflict):
2.657 + scopes = scope.scopes.union(new_scope.scopes)
2.658 + else:
2.659 + scopes = new_scope.scopes.union(set([scope]))
2.660 + elif isinstance(scope, ScopeConflict):
2.661 + scopes = scope.scopes.union(set([new_scope]))
2.662 + else:
2.663 + scopes = set([scope, new_scope])
2.664 + new_scope_usage[name] = ScopeConflict(scopes)
2.665 +
2.666 + self.scope_usage[-1] = new_scope_usage
2.667 +
2.668 + def _resume_broken_branches(self):
2.669 +
2.670 + """
2.671 + Incorporate users from suspended broken branches into the current set of
2.672 + active users.
2.673 + """
2.674 +
2.675 + suspended_users = self.suspended_broken_users.pop()
2.676 + current_users = self.attribute_users[-1]
2.677 + new_users = merge_mapping_dicts(suspended_users + [current_users])
2.678 + self.attribute_users[-1] = new_users
2.679 +
2.680 + def _resume_continuing_branches(self):
2.681 +
2.682 + """
2.683 + Incorporate users from suspended continuing branches into the current
2.684 + set of active users, merging usage from the latter with the former.
2.685 + """
2.686 +
2.687 + loop_node, suspended_users = self.suspended_continuing_users.pop()
2.688 + current_users = self.attribute_users[-1]
2.689 +
2.690 + # Connect the suspended users to the loop node.
2.691 +
2.692 + for users in suspended_users:
2.693 + self._connect_users_to_branch(users, loop_node)
2.694 +
2.695 + # Merge suspended branches with the current branch.
2.696 +
2.697 + new_users = merge_mapping_dicts(suspended_users + [current_users])
2.698 + self.attribute_users[-1] = new_users
2.699 +
2.700 + def _resume_abandoned_branches(self):
2.701 +
2.702 + """
2.703 + Incorporate users from abandoned branches into the current set of active
2.704 + users. The abandoned branches are taken from the containing branch.
2.705 + """
2.706 +
2.707 + current_users = self.attribute_users[-1]
2.708 + abandoned_users = self.abandoned_users[-2]
2.709 + new_users = merge_mapping_dicts(abandoned_users + [current_users])
2.710 + self.attribute_users[-1] = new_users
2.711 +
2.712 +# Special helper classes for usage and scope resolution.
2.713 +
2.714 +class EmptyDict:
2.715 +
2.716 + "A class providing dictionaries which retain no information."
2.717 +
2.718 + def has_key(self, name):
2.719 + return False
2.720 +
2.721 + def __setitem__(self, name, value):
2.722 + pass
2.723 +
2.724 + def __getitem__(self, name):
2.725 + raise KeyError, name
2.726 +
2.727 + def get(self, name, default=None):
2.728 + return default
2.729 +
2.730 + def keys(self):
2.731 + return []
2.732 +
2.733 + values = items = keys
2.734 +
2.735 +class AbandonedBranchScope(EmptyDict):
2.736 +
2.737 + """
2.738 + A class providing a value or state for an abandoned branch distinct from an
2.739 + empty scope dictionary.
2.740 + """
2.741 +
2.742 + pass
2.743 +
2.744 +abandoned_branch_scope = AbandonedBranchScope()
2.745 +
2.746 +class ScopeConflict:
2.747 +
2.748 + """
2.749 + A scope conflict caused when different code branches contribute different
2.750 + sources of names.
2.751 + """
2.752 +
2.753 + def __init__(self, scopes):
2.754 + self.scopes = scopes
2.755 +
2.756 +class UnsetType:
2.757 +
2.758 + "A None-like value."
2.759 +
2.760 + def __nonzero__(self):
2.761 + return False
2.762 +
2.763 +Unset = UnsetType()
2.764 +
2.765 +# vim: tabstop=4 expandtab shiftwidth=4
3.1 --- a/micropython/data.py Tue Nov 06 00:12:46 2012 +0100
3.2 +++ b/micropython/data.py Tue Nov 06 00:16:26 2012 +0100
3.3 @@ -52,12 +52,9 @@
3.4 where each such object is defined.
3.5 """
3.6
3.7 -from compiler.ast import AttributeUser
3.8 from micropython.program import ReplaceableContext, PlaceholderContext
3.9 from micropython.basicdata import *
3.10 -from micropython.errors import *
3.11 -from micropython.objectset import *
3.12 -from micropython.types import *
3.13 +from micropython.branch import BranchTracking
3.14 import sys
3.15
3.16 try:
3.17 @@ -65,45 +62,19 @@
3.18 except NameError:
3.19 from sets import Set as set
3.20
3.21 -class NamespaceDict(Namespace):
3.22 +class NamespaceDict(Namespace, BranchTracking):
3.23
3.24 "A mix-in providing dictionary methods."
3.25
3.26 def __init__(self, module=None):
3.27 + BranchTracking.__init__(self)
3.28 +
3.29 self.module = module
3.30 self.namespace = {}
3.31 self.globals = set()
3.32 self.lambdas = {} # only really useful for functions
3.33 self.finalised = False
3.34
3.35 - # Attributes accessed on objects, potentially narrowing their types.
3.36 - # Specific namespaces should define known names during initialisation.
3.37 - # For example, functions can define names belonging to parameters.
3.38 -
3.39 - # Attribute users, defining names which use attributes.
3.40 -
3.41 - self.attribute_users = [{}] # stack of assignments and branches
3.42 - self.attribute_user_shelves = []
3.43 -
3.44 - # Suspended user details plus loop details.
3.45 -
3.46 - self.suspended_broken_users = [] # stack of lists of user dicts
3.47 - self.suspended_continuing_users = [] # stack of lists of user dicts
3.48 -
3.49 - # Scope usage, indicating the origin of names.
3.50 -
3.51 - self.scope_usage = [{}] # stack of scope usage
3.52 - self.scope_shelves = []
3.53 -
3.54 - # Abandoned usage, useful for reviving usage for exception handlers.
3.55 -
3.56 - self.abandoned_users = [[]] # stack of lists of users
3.57 -
3.58 - # Define attribute usage to identify active program sections.
3.59 - # Attribute users are AST nodes defining names.
3.60 -
3.61 - self.all_attribute_users = set()
3.62 -
3.63 # Attribute/name definition and access.
3.64
3.65 def __delitem__(self, name):
3.66 @@ -127,6 +98,16 @@
3.67 def get(self, name, default=None):
3.68 return self.namespace.get(name, default)
3.69
3.70 + # Introspection methods.
3.71 +
3.72 + def is_method(self):
3.73 +
3.74 + """
3.75 + Return whether this function is a method explicitly defined in a class.
3.76 + """
3.77 +
3.78 + return False
3.79 +
3.80 # Administrative methods.
3.81
3.82 def finalise(self, objtable):
3.83 @@ -356,680 +337,6 @@
3.84
3.85 self.finalised = False
3.86
3.87 - # Attribute usage methods.
3.88 -
3.89 - def finalise_attribute_usage(self):
3.90 -
3.91 - "Propagate attribute usage for the namespace to the importer."
3.92 -
3.93 - module = self.module
3.94 - importer = module and module.importer
3.95 -
3.96 - if importer is not None:
3.97 -
3.98 - # Visit each user and examine the attribute usage for each name.
3.99 -
3.100 - for user in self.all_attribute_users:
3.101 -
3.102 - # First, visit the contributors and combine their attribute
3.103 - # usage with the usage recorded directly on the user.
3.104 -
3.105 - self.get_usage_from_contributors(user)
3.106 -
3.107 - # Record the defining user on each contributor.
3.108 -
3.109 - for contributor in user._attrcontributors:
3.110 - contributor._attrdefs.append(user)
3.111 -
3.112 - # Then, tell the importer about the usage.
3.113 -
3.114 - for name in user._attrnames.keys():
3.115 -
3.116 - # Only provide information about names defined by this user.
3.117 -
3.118 - usage = user._attrcombined.get(name, [])
3.119 -
3.120 - # Skip reporting where no actual usage occurs.
3.121 -
3.122 - if usage is None:
3.123 - continue
3.124 -
3.125 - # Eliminate non-usage.
3.126 -
3.127 - importer.use_names(user, name, tuple([attrnames for attrnames in usage if attrnames]), self.full_name())
3.128 -
3.129 - def finalise_users(self, objtable):
3.130 -
3.131 - "Record the object types for generating guards."
3.132 -
3.133 - # Visit each user and examine the attribute usage for each name.
3.134 -
3.135 - for user in self.all_attribute_users:
3.136 - user._attrtypes = self._deduce_types(user._attrcombined, objtable)
3.137 - self._finalise_contributor(user, objtable)
3.138 -
3.139 - def _finalise_contributors(self, node, objtable):
3.140 -
3.141 - """
3.142 - Visit the contributing branches of 'node', finalising them using the
3.143 - given 'objtable'.
3.144 - """
3.145 -
3.146 - for contributor in node._attrbranches:
3.147 - self._finalise_contributor(contributor, objtable)
3.148 -
3.149 - def _finalise_contributor(self, node, objtable):
3.150 -
3.151 - """
3.152 - Record the specific object types being used in various regions of a
3.153 - program unit.
3.154 - """
3.155 -
3.156 - if node._attrspecifictypes is None:
3.157 - merged = {}
3.158 -
3.159 - # Get the combined usage information from the user definitions.
3.160 -
3.161 - for user in node._attrdefs or [node]:
3.162 -
3.163 - # Filter the usage for each name using the local usage
3.164 - # information.
3.165 -
3.166 - for name, usage in user._attrcombined.items():
3.167 - localusage = node._attrnames.get(name)
3.168 -
3.169 - if usage and localusage:
3.170 - if not merged.has_key(name):
3.171 - merged[name] = ObjectSet()
3.172 -
3.173 - for attrnames, value in usage.items():
3.174 - if attrnames and localusage.issubset(attrnames):
3.175 - merged[name][attrnames] = value
3.176 -
3.177 - node._attrmerged = merged
3.178 - node._attrspecifictypes = self._deduce_types(node._attrmerged, objtable)
3.179 -
3.180 - self._finalise_contributors(node, objtable)
3.181 -
3.182 - def _deduce_types(self, usage, objtable):
3.183 -
3.184 - """
3.185 - Deduce the types for names from the given attribute 'usage' and using
3.186 - the given 'objtable'.
3.187 - """
3.188 -
3.189 - attrtypes = {}
3.190 - for name, combined_usage in usage.items():
3.191 - if combined_usage is not None:
3.192 - objtypes = get_object_types_for_usage(combined_usage, objtable, name, self.full_name(), True, self.module.importer)
3.193 - if objtypes:
3.194 - if isinstance(self, Function) and self.is_method() and name == "self":
3.195 - objtypes = filter_using_self(objtypes, self.parent)
3.196 - attrtypes[name] = objtypes
3.197 - return attrtypes
3.198 -
3.199 - def get_usage_from_contributors(self, node):
3.200 -
3.201 - """
3.202 - Obtain usage information from the given 'node', combined with usage
3.203 - details from its contributors, returning a tuple containing a set of all
3.204 - contributors employed along with a dictionary mapping names to lists of
3.205 - usage possibilities (each a collection of attribute names).
3.206 - """
3.207 -
3.208 - unfinished = {}
3.209 -
3.210 - # Process any unprocessed contributors, indicating the unfinished state
3.211 - # of the associated data.
3.212 -
3.213 - if node._attrcombined is None:
3.214 - node._attrcombined = Unset
3.215 -
3.216 - for contributor in node._attrbranches:
3.217 -
3.218 - # Get contributor details.
3.219 -
3.220 - unfinished_contributors = self.get_usage_from_contributors(contributor)
3.221 -
3.222 - # Collect unfinished contributors and affected nodes.
3.223 -
3.224 - # Where the contributor is already set to Unset, a loop has
3.225 - # occurred and this node will need to have its usage
3.226 - # recalculated later for the unfinished contributor.
3.227 -
3.228 - if contributor._attrcombined is Unset:
3.229 - if not unfinished.has_key(contributor):
3.230 - unfinished[contributor] = []
3.231 - unfinished[contributor].append(node)
3.232 - continue
3.233 -
3.234 - # Where the contributor provides usage details, it may also
3.235 - # communicate unfinished contributor information. As a
3.236 - # consequence, this node is also affected.
3.237 -
3.238 - for unfinished_contributor, nodes in unfinished_contributors.items():
3.239 - if not unfinished.has_key(unfinished_contributor):
3.240 - unfinished[unfinished_contributor] = nodes
3.241 - else:
3.242 - unfinished[unfinished_contributor] += nodes
3.243 -
3.244 - if node not in unfinished[unfinished_contributor]:
3.245 - unfinished[unfinished_contributor].append(node)
3.246 -
3.247 - # Set the current state of the usage on this node.
3.248 -
3.249 - node._attrcombined, node._attrcontributors = \
3.250 - self.get_usage_from_contributors_for_node(node)
3.251 -
3.252 - # Complete unfinished contributors relying on this node.
3.253 -
3.254 - if unfinished.has_key(node):
3.255 - processed = set()
3.256 - for contributor in unfinished[node]:
3.257 - if not contributor in processed:
3.258 - processed.add(contributor)
3.259 -
3.260 - contributor._attrcombined, contributor._attrcontributors = \
3.261 - self.get_usage_from_contributors_for_node(contributor)
3.262 -
3.263 - del unfinished[node]
3.264 -
3.265 - return unfinished
3.266 -
3.267 - def get_usage_from_contributors_for_node(self, node):
3.268 -
3.269 - # Visit each contributor, gathering usage for each name.
3.270 -
3.271 - contributor_usage = {}
3.272 - all_contributions = []
3.273 - all_contributors = set()
3.274 -
3.275 - for contributor in node._attrbranches:
3.276 - usage = contributor._attrcombined
3.277 - if usage is not Unset:
3.278 - all_contributions.append(usage)
3.279 -
3.280 - all_contributors.add(contributor)
3.281 - contributors = contributor._attrcontributors
3.282 - if contributors is not None:
3.283 - all_contributors.update(contributors)
3.284 -
3.285 - # Get contributed usage for each contributor.
3.286 - # This gathers usage for each name such as {(a, b), (c, d)} and
3.287 - # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}.
3.288 -
3.289 - update_mapping_dict(contributor_usage, all_contributions)
3.290 -
3.291 - # Then get the resulting usage.
3.292 - # First, make the current usage compatible with the contributed
3.293 - # usage: this makes the attribute usage for each name merely one
3.294 - # member in a list of many possibilities.
3.295 - # Then, combine the current usage with the contributed usage.
3.296 - # Thus, usage of {(f, g)} combined with {(a, b), (c, d)} would give
3.297 - # {(f, g, a, b), (f, g, c, d)}.
3.298 -
3.299 - return combine_mapping_dicts(deepen_mapping_dict(node._attrnames), contributor_usage), all_contributors
3.300 -
3.301 - def use_attribute(self, name, attrname, value=None):
3.302 -
3.303 - """
3.304 - Note usage on the attribute user 'name' of the attribute 'attrname',
3.305 - noting an assignment if 'value' is specified.
3.306 - """
3.307 -
3.308 - return self._use_attribute(name, attrname, value)
3.309 -
3.310 - def use_specific_attribute(self, objname, attrname):
3.311 -
3.312 - "Declare the usage on 'objname' of the given 'attrname'."
3.313 -
3.314 - self._use_specific_attribute(objname, attrname)
3.315 -
3.316 - # These shadow various methods in the InspectedModule class, and provide
3.317 - # implementations generally.
3.318 -
3.319 - def _use_specific_attribute(self, objname, attrname, from_name=None):
3.320 -
3.321 - """
3.322 - Note attribute usage specifically on 'objname' - an object which is
3.323 - known at inspection time - or in the current unit if 'objname' is None,
3.324 - nominating a specific attribute 'attrname'.
3.325 -
3.326 - This bypasses attribute user mechanisms.
3.327 - """
3.328 -
3.329 - from_name = from_name or self.full_name()
3.330 - objname = objname or from_name
3.331 - module = self.module
3.332 - importer = module and module.importer
3.333 -
3.334 - if importer is not None:
3.335 - importer.use_specific_name(objname, attrname, from_name)
3.336 -
3.337 - def _use_attribute(self, name, attrname, value=None):
3.338 -
3.339 - """
3.340 - Indicate the use of the given 'name' in this namespace of an attribute
3.341 - with the given 'attrname'. If the optional 'value' is specified, an
3.342 - assignment using the given 'value' is recorded.
3.343 - """
3.344 -
3.345 - users = self.attribute_users[-1]
3.346 -
3.347 - # If no users are defined for the name, it cannot be handled.
3.348 -
3.349 - if not users.has_key(name):
3.350 - return []
3.351 -
3.352 - # Add the usage to all current users.
3.353 -
3.354 - for user in users[name]:
3.355 - values = user._attrnames[name]
3.356 - if values is None:
3.357 - values = user._attrnames[name] = ObjectSet()
3.358 -
3.359 - # Add an entry for the attribute, optionally with an assigned
3.360 - # value.
3.361 -
3.362 - values.add(attrname)
3.363 - if value is not None:
3.364 - values[attrname].add(value)
3.365 -
3.366 - return users[name]
3.367 -
3.368 - def _define_attribute_user(self, node):
3.369 -
3.370 - """
3.371 - Define 'node' as the user of attributes, indicating the point where the
3.372 - user is defined.
3.373 - """
3.374 -
3.375 - name = node.name
3.376 - self._define_attribute_user_for_name(node, name)
3.377 -
3.378 - def _define_attribute_user_for_name(self, node, name):
3.379 -
3.380 - "Define 'node' as the user of attributes for the given 'name'."
3.381 -
3.382 - users = self.attribute_users[-1]
3.383 -
3.384 - # This node overrides previous definitions.
3.385 -
3.386 - users[name] = set([node])
3.387 -
3.388 - # Record the attribute combinations for the name.
3.389 -
3.390 - self._init_attribute_user_for_name(node, name)
3.391 -
3.392 - # Remember this user.
3.393 -
3.394 - self.all_attribute_users.add(node)
3.395 -
3.396 - def _init_attribute_user_for_name(self, node, name):
3.397 -
3.398 - "Make sure that 'node' is initialised for 'name'."
3.399 -
3.400 - self._init_attribute_user(node)
3.401 - node._attrnames[name] = None
3.402 -
3.403 - def _init_attribute_user(self, node):
3.404 -
3.405 - # Attribute usage for names.
3.406 -
3.407 - if node._attrnames is None:
3.408 - node._attrnames = {}
3.409 - node._attrmerged = {}
3.410 -
3.411 - # Branches contributing usage to this node.
3.412 -
3.413 - if node._attrbranches is None:
3.414 - node._attrbranches = []
3.415 -
3.416 - # Definitions receiving usage from this node.
3.417 -
3.418 - if node._attrdefs is None:
3.419 - node._attrdefs = []
3.420 -
3.421 - def _define_attribute_accessor(self, name, attrname, node, value):
3.422 -
3.423 - # NOTE: Revisiting of nodes may occur for loops.
3.424 -
3.425 - if node._attrusers is None:
3.426 - node._attrusers = set()
3.427 -
3.428 - node._attrusers.update(self.use_attribute(name, attrname, value))
3.429 - node._username = name
3.430 -
3.431 - # Branch management methods.
3.432 -
3.433 - def _new_branchpoint(self, loop_node=None):
3.434 -
3.435 - """
3.436 - Establish a new branchpoint where several control-flow branches diverge
3.437 - and subsequently converge.
3.438 - """
3.439 -
3.440 - self.attribute_user_shelves.append([])
3.441 - self.scope_shelves.append([])
3.442 -
3.443 - if loop_node is not None:
3.444 - self.suspended_broken_users.append([])
3.445 - self.suspended_continuing_users.append((loop_node, []))
3.446 -
3.447 - def _new_branch(self, node):
3.448 -
3.449 - """
3.450 - Establish a new control-flow branch, transferring attribute usage to
3.451 - the new branch so that it may be augmented for each name locally.
3.452 -
3.453 - Add the given 'node' as an active user to be informed of attribute
3.454 - usage.
3.455 - """
3.456 -
3.457 - attribute_users = self.attribute_users[-1]
3.458 -
3.459 - # Define this node as the active attribute user for all currently
3.460 - # defined names.
3.461 -
3.462 - new_users = {}
3.463 -
3.464 - for name in attribute_users.keys():
3.465 - new_users[name] = [node]
3.466 - self._init_attribute_user_for_name(node, name)
3.467 -
3.468 - self._init_attribute_user(node)
3.469 - self.attribute_users.append(new_users)
3.470 -
3.471 - # Add this user as a contributor to the previously active users.
3.472 -
3.473 - self._connect_users_to_branch(attribute_users, node)
3.474 -
3.475 - # Retain a record of scope usage.
3.476 -
3.477 - scope_usage = {}
3.478 - scope_usage.update(self.scope_usage[-1])
3.479 - self.scope_usage.append(scope_usage)
3.480 -
3.481 - # Retain a record of abandoned branch users.
3.482 -
3.483 - self.abandoned_users.append([])
3.484 -
3.485 - def _connect_users_to_branch(self, attribute_users, node):
3.486 -
3.487 - """
3.488 - Given the 'attribute_users' mapping, connect the users referenced in the
3.489 - mapping to the given branch 'node'.
3.490 - """
3.491 -
3.492 - all_users = set()
3.493 -
3.494 - for users in attribute_users.values():
3.495 - all_users.update(users)
3.496 -
3.497 - for user in all_users:
3.498 - self._init_attribute_user(user)
3.499 - user._attrbranches.append(node)
3.500 -
3.501 - def _abandon_branch(self, retain_branch=True):
3.502 -
3.503 - """
3.504 - Abandon scope usage, permitting locally different scopes for names,
3.505 - provided these cannot "escape" from the branch.
3.506 - """
3.507 -
3.508 - attribute_users = self.attribute_users[-1]
3.509 -
3.510 - self.attribute_users[-1] = {}
3.511 - self.scope_usage[-1] = abandoned_branch_scope
3.512 -
3.513 - if retain_branch:
3.514 - self.abandoned_users[-1].append(attribute_users)
3.515 -
3.516 - def _suspend_broken_branch(self):
3.517 -
3.518 - """
3.519 - Suspend a branch for resumption after the current loop.
3.520 - """
3.521 -
3.522 - attribute_users = self.attribute_users[-1]
3.523 -
3.524 - users = self.suspended_broken_users[-1]
3.525 - users.append(attribute_users)
3.526 - self._abandon_branch(False)
3.527 -
3.528 - def _suspend_continuing_branch(self):
3.529 -
3.530 - """
3.531 - Suspend a branch for resumption after the current iteration.
3.532 - """
3.533 -
3.534 - attribute_users = self.attribute_users[-1]
3.535 -
3.536 - loop_node, users = self.suspended_continuing_users[-1]
3.537 - users.append(attribute_users)
3.538 - self._abandon_branch(False)
3.539 -
3.540 - def _shelve_branch(self):
3.541 -
3.542 - """
3.543 - Shelve the current control-flow branch, recording the attribute usage
3.544 - for subsequent merging. If this branch should be abandoned, the usage
3.545 - observations are still recorded but will not contribute to subsequent
3.546 - observations after a merge.
3.547 - """
3.548 -
3.549 - users = self.attribute_users.pop()
3.550 - self.attribute_user_shelves[-1].append(users)
3.551 -
3.552 - scope_usage = self.scope_usage.pop()
3.553 - self.scope_shelves[-1].append(scope_usage)
3.554 -
3.555 - def _merge_branches(self):
3.556 -
3.557 - """
3.558 - Merge control-flow branches. This should find the users active within
3.559 - each branch, which have been "shelved", and update the active users
3.560 - dictionary with these contributions.
3.561 - """
3.562 -
3.563 - # Combine the attribute users. This ensures that a list of users
3.564 - # affected by attribute usage is maintained for the current branch.
3.565 -
3.566 - all_shelved_users = self.attribute_user_shelves.pop()
3.567 - new_users = merge_mapping_dicts(all_shelved_users)
3.568 - self.attribute_users[-1] = new_users
3.569 -
3.570 - # Abandoned users are retained for exception handling purposes.
3.571 -
3.572 - all_abandoned_users = self.abandoned_users.pop()
3.573 - new_abandoned_users = merge_mapping_dicts(all_abandoned_users)
3.574 - self.abandoned_users[-1].append(new_abandoned_users)
3.575 -
3.576 - # Combine the scope usage.
3.577 -
3.578 - scope_usage = self.scope_usage[-1]
3.579 - new_scope_usage = {}
3.580 -
3.581 - all_scope_usage = self.scope_shelves.pop()
3.582 - all_scope_names = set()
3.583 -
3.584 - # Find all the names for whom scope information has been defined.
3.585 -
3.586 - for shelved_usage in all_scope_usage:
3.587 - all_scope_names.update(shelved_usage.keys())
3.588 -
3.589 - for shelved_usage in all_scope_usage:
3.590 - for name in all_scope_names:
3.591 -
3.592 - # Find the recorded scope for the name.
3.593 -
3.594 - if shelved_usage.has_key(name):
3.595 - scope = shelved_usage[name]
3.596 - elif scope_usage.has_key(name):
3.597 - scope = scope_usage[name]
3.598 -
3.599 - # For abandoned branches, no scope is asserted for a name.
3.600 -
3.601 - elif isinstance(shelved_usage, AbandonedBranchScope):
3.602 - scope = None
3.603 -
3.604 - # If no scope is recorded, find a suitable external source.
3.605 -
3.606 - else:
3.607 - attr, scope, full_name = self._get_with_scope(name, external=1)
3.608 -
3.609 - # Attempt to record the scope, testing for conflicts.
3.610 -
3.611 - if scope:
3.612 - if not new_scope_usage.has_key(name):
3.613 - new_scope_usage[name] = scope
3.614 - else:
3.615 - new_scope = new_scope_usage[name]
3.616 - if new_scope != scope:
3.617 - if isinstance(new_scope, ScopeConflict):
3.618 - if isinstance(scope, ScopeConflict):
3.619 - scopes = scope.scopes.union(new_scope.scopes)
3.620 - else:
3.621 - scopes = new_scope.scopes.union(set([scope]))
3.622 - elif isinstance(scope, ScopeConflict):
3.623 - scopes = scope.scopes.union(set([new_scope]))
3.624 - else:
3.625 - scopes = set([scope, new_scope])
3.626 - new_scope_usage[name] = ScopeConflict(scopes)
3.627 -
3.628 - self.scope_usage[-1] = new_scope_usage
3.629 -
3.630 - def _resume_broken_branches(self):
3.631 -
3.632 - """
3.633 - Incorporate users from suspended broken branches into the current set of
3.634 - active users.
3.635 - """
3.636 -
3.637 - suspended_users = self.suspended_broken_users.pop()
3.638 - current_users = self.attribute_users[-1]
3.639 - new_users = merge_mapping_dicts(suspended_users + [current_users])
3.640 - self.attribute_users[-1] = new_users
3.641 -
3.642 - def _resume_continuing_branches(self):
3.643 -
3.644 - """
3.645 - Incorporate users from suspended continuing branches into the current
3.646 - set of active users, merging usage from the latter with the former.
3.647 - """
3.648 -
3.649 - loop_node, suspended_users = self.suspended_continuing_users.pop()
3.650 - current_users = self.attribute_users[-1]
3.651 -
3.652 - # Connect the suspended users to the loop node.
3.653 -
3.654 - for users in suspended_users:
3.655 - self._connect_users_to_branch(users, loop_node)
3.656 -
3.657 - # Merge suspended branches with the current branch.
3.658 -
3.659 - new_users = merge_mapping_dicts(suspended_users + [current_users])
3.660 - self.attribute_users[-1] = new_users
3.661 -
3.662 - def _resume_abandoned_branches(self):
3.663 -
3.664 - """
3.665 - Incorporate users from abandoned branches into the current set of active
3.666 - users. The abandoned branches are taken from the containing branch.
3.667 - """
3.668 -
3.669 - current_users = self.attribute_users[-1]
3.670 - abandoned_users = self.abandoned_users[-2]
3.671 - new_users = merge_mapping_dicts(abandoned_users + [current_users])
3.672 - self.attribute_users[-1] = new_users
3.673 -
3.674 - # Scope usage methods.
3.675 -
3.676 - def define_scope(self, name, scope):
3.677 -
3.678 - """
3.679 - Define 'name' as being from the given 'scope' in the current namespace.
3.680 - """
3.681 -
3.682 - self.scope_usage[-1][name] = scope
3.683 -
3.684 - def note_scope(self, name, scope):
3.685 -
3.686 - """
3.687 - Note usage of 'name' from the given 'scope' in the current namespace.
3.688 - If a conflict has been recorded previously, raise an exception.
3.689 - """
3.690 -
3.691 - scope_usage = self.scope_usage[-1]
3.692 -
3.693 - if scope_usage.has_key(name):
3.694 - found_scope = scope_usage[name]
3.695 - if isinstance(found_scope, ScopeConflict):
3.696 - raise InspectError("Scope conflict for %r: defined as %s." % (
3.697 - name, ", ".join(found_scope.scopes)))
3.698 -
3.699 - scope_usage[name] = scope
3.700 -
3.701 - def used_in_scope(self, name, scope):
3.702 -
3.703 - """
3.704 - Return whether 'name' is used from the given 'scope' in the current
3.705 - namespace.
3.706 - """
3.707 -
3.708 - scope_usage = self.scope_usage[-1]
3.709 - return scope_usage.get(name) == scope
3.710 -
3.711 -# Special helper classes for usage and scope resolution.
3.712 -
3.713 -class EmptyDict:
3.714 -
3.715 - "A class providing dictionaries which retain no information."
3.716 -
3.717 - def has_key(self, name):
3.718 - return False
3.719 -
3.720 - def __setitem__(self, name, value):
3.721 - pass
3.722 -
3.723 - def __getitem__(self, name):
3.724 - raise KeyError, name
3.725 -
3.726 - def get(self, name, default=None):
3.727 - return default
3.728 -
3.729 - def keys(self):
3.730 - return []
3.731 -
3.732 - values = items = keys
3.733 -
3.734 -class AbandonedBranchScope(EmptyDict):
3.735 -
3.736 - """
3.737 - A class providing a value or state for an abandoned branch distinct from an
3.738 - empty scope dictionary.
3.739 - """
3.740 -
3.741 - pass
3.742 -
3.743 -abandoned_branch_scope = AbandonedBranchScope()
3.744 -
3.745 -class ScopeConflict:
3.746 -
3.747 - """
3.748 - A scope conflict caused when different code branches contribute different
3.749 - sources of names.
3.750 - """
3.751 -
3.752 - def __init__(self, scopes):
3.753 - self.scopes = scopes
3.754 -
3.755 -class NullBranch(AttributeUser):
3.756 -
3.757 - "A class representing an attribute user for a non-existent branch."
3.758 -
3.759 - pass
3.760 -
3.761 # Program data structures.
3.762
3.763 class Attr:
3.764 @@ -2227,13 +1534,4 @@
3.765 def __repr__(self):
3.766 return "AtLeast(%r)" % self.count
3.767
3.768 -class UnsetType:
3.769 -
3.770 - "A None-like value."
3.771 -
3.772 - def __nonzero__(self):
3.773 - return False
3.774 -
3.775 -Unset = UnsetType()
3.776 -
3.777 # vim: tabstop=4 expandtab shiftwidth=4
4.1 --- a/micropython/inspect.py Tue Nov 06 00:12:46 2012 +0100
4.2 +++ b/micropython/inspect.py Tue Nov 06 00:16:26 2012 +0100
4.3 @@ -78,6 +78,12 @@
4.4 import compiler.ast
4.5 import sys
4.6
4.7 +class NullBranch(compiler.ast.AttributeUser):
4.8 +
4.9 + "A class representing an attribute user for a non-existent branch."
4.10 +
4.11 + pass
4.12 +
4.13 # Program visitors.
4.14
4.15 class InspectedModule(ASTVisitor, Module):