1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/branching.py Tue Aug 30 16:51:10 2016 +0200
1.3 @@ -0,0 +1,818 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Track attribute usage for names.
1.8 +
1.9 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,
1.10 + 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
1.11 +
1.12 +This program is free software; you can redistribute it and/or modify it under
1.13 +the terms of the GNU General Public License as published by the Free Software
1.14 +Foundation; either version 3 of the License, or (at your option) any later
1.15 +version.
1.16 +
1.17 +This program is distributed in the hope that it will be useful, but WITHOUT
1.18 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.19 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.20 +details.
1.21 +
1.22 +You should have received a copy of the GNU General Public License along with
1.23 +this program. If not, see <http://www.gnu.org/licenses/>.
1.24 +"""
1.25 +
1.26 +from common import dict_for_keys, init_item
1.27 +
1.28 +class Branch:
1.29 +
1.30 + """
1.31 + A control-flow branch capturing local attribute usage for names.
1.32 + Branches typically begin with assignments or function parameters and are
1.33 + connected to others introduced by conditional and loop nodes.
1.34 +
1.35 + Branches hosting accesses, and thus providing usage information, are
1.36 + contributors to preceding branches.
1.37 +
1.38 + Branches also provide a route from accesses back to assignments which are
1.39 + the ultimate suppliers of the names involved.
1.40 + """
1.41 +
1.42 + def __init__(self, names, assigning=False, values=None):
1.43 +
1.44 + """
1.45 + Capture attribute usage for the given 'names', with the accompanying
1.46 + 'values' indicating assigned values for each name, if indicated.
1.47 + """
1.48 +
1.49 + self.contributors = set()
1.50 + self.suppliers = {}
1.51 + self.assignments = set(assigning and names or [])
1.52 + self.usage = {}
1.53 + self.values = {}
1.54 +
1.55 + # Initialise usage for each name.
1.56 +
1.57 + for name in names:
1.58 + self.usage[name] = set()
1.59 +
1.60 + # Initialise assigned values if any were provided.
1.61 +
1.62 + if values:
1.63 + for name, value in zip(names, values):
1.64 + if value:
1.65 + self.values[name] = value
1.66 +
1.67 + # Computed results.
1.68 +
1.69 + self.combined_usage = None
1.70 +
1.71 + def get_assignment_sources(self, name):
1.72 +
1.73 + """
1.74 + Return the sources of 'name' from this branch's assignment information,
1.75 + returning a list containing only this branch itself if it is the source.
1.76 + """
1.77 +
1.78 + if name in self.assignments:
1.79 + return [self]
1.80 + else:
1.81 + return [b for b in self.get_all_suppliers(name) if name in b.assignments]
1.82 +
1.83 + def set_usage(self, name, attrname):
1.84 +
1.85 + """
1.86 + Record usage on the given 'name' of the attribute 'attrname'.
1.87 + """
1.88 +
1.89 + if self.usage.has_key(name):
1.90 + self.usage[name].add(attrname)
1.91 +
1.92 + def get_usage(self):
1.93 +
1.94 + """
1.95 + Obtain usage from this node, combined with usage observed by its
1.96 + contributors. Unlike the local usage which involves only a single set of
1.97 + attribute names for a given variable name, the returned usage is a set
1.98 + of attribute name combinations for a given variable name. For example:
1.99 +
1.100 + {'a': set([('p', 'q', 'r'), ('p', 'r')])}
1.101 + """
1.102 +
1.103 + if self.combined_usage is None:
1.104 +
1.105 + # Accumulate usage observations from contributors.
1.106 +
1.107 + all_usage = []
1.108 +
1.109 + for contributor in self.contributors:
1.110 +
1.111 + # Record any usage that can be returned.
1.112 +
1.113 + all_usage.append(contributor.get_usage())
1.114 +
1.115 + # Merge usage from the contributors.
1.116 +
1.117 + merged_usage = merge_dicts(all_usage)
1.118 +
1.119 + # Make the local usage compatible with the combined usage.
1.120 +
1.121 + usage = deepen_dict(self.usage)
1.122 +
1.123 + self.combined_usage = combine_dicts(usage, merged_usage, combine_sets)
1.124 +
1.125 + return self.combined_usage
1.126 +
1.127 + def get_all_suppliers(self, name, all_suppliers=None):
1.128 +
1.129 + "Return all branches supplying this branch with definitions of 'name'."
1.130 +
1.131 + all_suppliers = all_suppliers or set()
1.132 + all_suppliers.add(self)
1.133 +
1.134 + if self.suppliers.has_key(name):
1.135 + for supplier in self.suppliers[name]:
1.136 + if supplier not in all_suppliers:
1.137 + supplier.get_all_suppliers(name, all_suppliers)
1.138 +
1.139 + return all_suppliers
1.140 +
1.141 + def __repr__(self):
1.142 + return "Branch(%r, %r)" % (self.usage.keys(),
1.143 + self.assignments and True or False)
1.144 +
1.145 +class BranchTracker:
1.146 +
1.147 + """
1.148 + A tracker of attribute usage for names in a namespace. This tracker directs
1.149 + usage observations to branches which are the ultimate repositories of
1.150 + attribute usage information.
1.151 +
1.152 + As a program unit is inspected, the branches associated with names may
1.153 + change. Assignments reset the branches; control-flow operations cause
1.154 + branches to be accumulated from different code paths.
1.155 + """
1.156 +
1.157 + def __init__(self):
1.158 +
1.159 + # Track assignments.
1.160 +
1.161 + self.assignments = {}
1.162 +
1.163 + # Details of attributes at each active branch level.
1.164 +
1.165 + self.attribute_branches = [{}] # stack of branches for names
1.166 + self.attribute_branch_shelves = [] # stack of shelved branches
1.167 +
1.168 + # Suspended branch details plus loop details.
1.169 +
1.170 + self.suspended_broken_branches = [] # stack of lists of dicts
1.171 + self.suspended_continuing_branches = [] # stack of lists of dicts
1.172 +
1.173 + # Abandoned usage, useful for reviving usage for exception handlers.
1.174 +
1.175 + self.abandoned_branches = [[]] # stack of lists of branches
1.176 +
1.177 + # Returning branches are like abandoned branches but are only revived in
1.178 + # finally clauses.
1.179 +
1.180 + self.returning_branches = [[]]
1.181 +
1.182 + # Branches active when starting loops.
1.183 +
1.184 + self.loop_branches = []
1.185 +
1.186 + # Inherited usage.
1.187 +
1.188 + self.inherited = None
1.189 +
1.190 + # Structure assembly methods.
1.191 +
1.192 + def new_branchpoint(self, loop_node=False):
1.193 +
1.194 + """
1.195 + Indicate that branches diverge, initialising resources dependent on
1.196 + any given 'loop_node'.
1.197 + """
1.198 +
1.199 + self.attribute_branch_shelves.append([])
1.200 +
1.201 + if loop_node:
1.202 + self.suspended_broken_branches.append([])
1.203 + self.suspended_continuing_branches.append([])
1.204 +
1.205 + # Retain a record of abandoned branches.
1.206 +
1.207 + self.abandoned_branches.append([])
1.208 + self.returning_branches.append([])
1.209 +
1.210 + def new_branch(self, loop_node=False):
1.211 +
1.212 + "Create a new branch."
1.213 +
1.214 + attribute_branches = self.attribute_branches[-1]
1.215 +
1.216 + branch, new_branches = self._new_branch(attribute_branches)
1.217 +
1.218 + if branch and loop_node:
1.219 + self.loop_branches.append(branch)
1.220 +
1.221 + # Start using the branch for known names.
1.222 +
1.223 + self.attribute_branches.append(new_branches)
1.224 +
1.225 + def _new_branch(self, attribute_branches):
1.226 +
1.227 + """
1.228 + Define a new branch that will record attribute usage on known names from
1.229 + 'attribute_branches'.
1.230 + """
1.231 +
1.232 + # Detect abandoned branches.
1.233 +
1.234 + if isinstance(attribute_branches, AbandonedDict):
1.235 + return None, AbandonedDict()
1.236 +
1.237 + # Otherwise, define a new branch.
1.238 +
1.239 + names = attribute_branches.keys()
1.240 +
1.241 + new_branches = {}
1.242 + branch = Branch(names)
1.243 +
1.244 + for name in names:
1.245 + new_branches[name] = [branch]
1.246 +
1.247 + # Add this new branch as a contributor to the previously active
1.248 + # branches.
1.249 +
1.250 + self._connect_branches(attribute_branches, branch)
1.251 +
1.252 + return branch, new_branches
1.253 +
1.254 + def shelve_branch(self, loop_node=False):
1.255 +
1.256 + "Retain the current branch for later merging."
1.257 +
1.258 + branches = self.attribute_branches.pop()
1.259 + self.attribute_branch_shelves[-1].append(branches)
1.260 +
1.261 + # Connect any loop branch to the active branches as contributors.
1.262 +
1.263 + if loop_node:
1.264 + branch = self.loop_branches.pop()
1.265 + self._connect_branches(branches, branch, loop_node)
1.266 +
1.267 + def abandon_branch(self):
1.268 +
1.269 + "Abandon the current branch, retaining it for later."
1.270 +
1.271 + attribute_branches = self.attribute_branches[-1]
1.272 + self._abandon_branch()
1.273 + self.abandoned_branches[-1].append(attribute_branches)
1.274 +
1.275 + def abandon_returning_branch(self):
1.276 +
1.277 + "Abandon the current branch, retaining it for later."
1.278 +
1.279 + attribute_branches = self.attribute_branches[-1]
1.280 + self._abandon_branch()
1.281 + self.returning_branches[-1].append(attribute_branches)
1.282 +
1.283 + def suspend_broken_branch(self):
1.284 +
1.285 + "Suspend a branch for breaking out of a loop."
1.286 +
1.287 + attribute_branches = self.attribute_branches[-1]
1.288 +
1.289 + branches = self.suspended_broken_branches[-1]
1.290 + branches.append(attribute_branches)
1.291 + self._abandon_branch()
1.292 +
1.293 + def suspend_continuing_branch(self):
1.294 +
1.295 + "Suspend a branch for loop continuation."
1.296 +
1.297 + attribute_branches = self.attribute_branches[-1]
1.298 +
1.299 + branches = self.suspended_continuing_branches[-1]
1.300 + branches.append(attribute_branches)
1.301 + self._abandon_branch()
1.302 +
1.303 + def _abandon_branch(self):
1.304 +
1.305 + "Abandon the current branch."
1.306 +
1.307 + self.attribute_branches[-1] = AbandonedDict()
1.308 +
1.309 + def resume_abandoned_branches(self):
1.310 +
1.311 + """
1.312 + Resume branches previously abandoned.
1.313 +
1.314 + Abandoned branches are not reset because they may not be handled by
1.315 + exception handlers after all.
1.316 + """
1.317 +
1.318 + current_branches = self.attribute_branches[-1]
1.319 + abandoned_branches = self.abandoned_branches[-1]
1.320 + merged_branches = merge_dicts(abandoned_branches + [current_branches])
1.321 +
1.322 + # Replace the combined branches with a new branch applying to all active
1.323 + # names, connected to the supplying branches.
1.324 +
1.325 + branch, new_branches = self._new_branch(merged_branches)
1.326 + self.attribute_branches.append(new_branches)
1.327 +
1.328 + # Although returning branches should not be considered as accumulating
1.329 + # usage, they do provide sources of assignments.
1.330 +
1.331 + if branch:
1.332 + for returning_branches in self.returning_branches[-1]:
1.333 + self._connect_suppliers(returning_branches, branch)
1.334 +
1.335 + def resume_all_abandoned_branches(self):
1.336 +
1.337 + """
1.338 + Resume branches previously abandoned including returning branches.
1.339 +
1.340 + Abandoned branches are not reset because they may not be handled by
1.341 + exception handlers after all.
1.342 + """
1.343 +
1.344 + current_branches = self.attribute_branches[-1]
1.345 + abandoned_branches = self.abandoned_branches[-1]
1.346 + returning_branches = self.returning_branches[-1]
1.347 + merged_branches = merge_dicts(abandoned_branches + returning_branches + [current_branches])
1.348 + self.replace_branches(merged_branches)
1.349 +
1.350 + # Return the previously-active branches for later restoration.
1.351 +
1.352 + return current_branches
1.353 +
1.354 + def resume_broken_branches(self):
1.355 +
1.356 + "Resume branches previously suspended for breaking out of a loop."
1.357 +
1.358 + suspended_branches = self.suspended_broken_branches.pop()
1.359 + current_branches = self.attribute_branches[-1]
1.360 +
1.361 + # Merge suspended branches with the current branch.
1.362 +
1.363 + merged_branches = merge_dicts(suspended_branches + [current_branches])
1.364 + self.replace_branches(merged_branches)
1.365 +
1.366 + def resume_continuing_branches(self):
1.367 +
1.368 + "Resume branches previously suspended for loop continuation."
1.369 +
1.370 + suspended_branches = self.suspended_continuing_branches.pop()
1.371 + current_branches = self.attribute_branches[-1]
1.372 +
1.373 + # Merge suspended branches with the current branch.
1.374 +
1.375 + merged_branches = merge_dicts(suspended_branches + [current_branches])
1.376 + self.replace_branches(merged_branches)
1.377 +
1.378 + def replace_branches(self, merged_branches):
1.379 +
1.380 + """
1.381 + Replace the 'merged_branches' with a new branch applying to all active
1.382 + names, connected to the supplying branches.
1.383 + """
1.384 +
1.385 + branch, new_branches = self._new_branch(merged_branches)
1.386 + self.attribute_branches[-1] = new_branches
1.387 +
1.388 + def restore_active_branches(self, branches):
1.389 +
1.390 + "Restore the active 'branches'."
1.391 +
1.392 + self.attribute_branches[-1] = branches
1.393 +
1.394 + def merge_branches(self):
1.395 +
1.396 + "Merge branches."
1.397 +
1.398 + # Combine the attribute branches. This ensures that a list of branches
1.399 + # affected by attribute usage is maintained for the current branch.
1.400 +
1.401 + all_shelved_branches = self.attribute_branch_shelves.pop()
1.402 + merged_branches = merge_dicts(all_shelved_branches, missing=make_missing)
1.403 + self.replace_branches(merged_branches)
1.404 +
1.405 + # Abandoned branches are retained for exception handling purposes.
1.406 +
1.407 + all_abandoned_branches = self.abandoned_branches.pop()
1.408 + new_abandoned_branches = merge_dicts(all_abandoned_branches)
1.409 + self.abandoned_branches[-1].append(new_abandoned_branches)
1.410 +
1.411 + # Returning branches are retained for finally clauses.
1.412 +
1.413 + all_returning_branches = self.returning_branches.pop()
1.414 + new_returning_branches = merge_dicts(all_returning_branches)
1.415 + self.returning_branches[-1].append(new_returning_branches)
1.416 +
1.417 + # Internal structure assembly methods.
1.418 +
1.419 + def _connect_branches(self, attribute_branches, contributor, loop_node=False):
1.420 +
1.421 + """
1.422 + Given the 'attribute_branches' mapping, connect the branches referenced
1.423 + in the mapping to the given 'contributor' branch. If 'loop_node' is
1.424 + set to a true value, connect only the branches so that the 'contributor'
1.425 + references the nodes supplying it with name information.
1.426 + """
1.427 +
1.428 + all_branches = self._connect_suppliers(attribute_branches, contributor)
1.429 + if not loop_node:
1.430 + self._connect_contributor(contributor, all_branches)
1.431 +
1.432 + def _connect_suppliers(self, attribute_branches, contributor):
1.433 +
1.434 + "Connect the 'attribute_branches' to the given 'contributor'."
1.435 +
1.436 + # Gather branches involved with all known names into a single set.
1.437 +
1.438 + all_branches = set()
1.439 +
1.440 + for name, branches in attribute_branches.items():
1.441 + all_branches.update(branches)
1.442 +
1.443 + # Also note receiving branches on the contributor.
1.444 +
1.445 + for branch in branches:
1.446 + init_item(contributor.suppliers, name, set)
1.447 + contributor.suppliers[name].add(branch)
1.448 +
1.449 + return all_branches
1.450 +
1.451 + def _connect_contributor(self, contributor, branches):
1.452 +
1.453 + "Connect the given 'contributor' branch to the given 'branches'."
1.454 +
1.455 + for branch in branches:
1.456 + branch.contributors.add(contributor)
1.457 +
1.458 + # Namespace methods.
1.459 +
1.460 + def inherit_branches(self, tracker, names):
1.461 +
1.462 + """
1.463 + Propagate branches from the given 'tracker' excluding those associated
1.464 + with 'names'.
1.465 + """
1.466 +
1.467 + # For each inherited name, create a branch connected to the inherited
1.468 + # branches.
1.469 +
1.470 + self.inherited = {}
1.471 +
1.472 + for name, branches in tracker.attribute_branches[-1].items():
1.473 +
1.474 + # Do not inherit any listed names (typically parameters) or any
1.475 + # special names.
1.476 +
1.477 + if name in names or name.startswith("$"):
1.478 + continue
1.479 +
1.480 + # Make a tentative assignment for the name.
1.481 +
1.482 + contributor = Branch([name], True)
1.483 + init_item(self.assignments, name, list)
1.484 + self.assignments[name].append(contributor)
1.485 +
1.486 + # Connect the inherited branch to the new one.
1.487 +
1.488 + for branch in branches:
1.489 + init_item(contributor.suppliers, name, set)
1.490 + contributor.suppliers[name].add(branch)
1.491 + branch.contributors.add(contributor)
1.492 +
1.493 + # Record the inherited branch.
1.494 +
1.495 + self.inherited[name] = [contributor]
1.496 +
1.497 + self.attribute_branches[-1].update(self.inherited)
1.498 +
1.499 + def disconnect_name(self, name):
1.500 +
1.501 + "Disconnect inherited branches for 'name'."
1.502 +
1.503 + if not self.inherited or not self.inherited.has_key(name):
1.504 + return
1.505 +
1.506 + # Remove the new branch from the inherited branches for the name.
1.507 +
1.508 + for contributor in self.inherited[name]:
1.509 + for supplier in contributor.suppliers[name]:
1.510 + supplier.contributors.remove(contributor)
1.511 + del contributor.suppliers[name]
1.512 +
1.513 + del self.inherited[name]
1.514 +
1.515 + # Attribute usage methods.
1.516 +
1.517 + def tracking_name(self, name):
1.518 +
1.519 + """
1.520 + Return whether 'name' is being tracked, returning all branches doing so
1.521 + if it is.
1.522 + """
1.523 +
1.524 + return self.assignments.has_key(name) and \
1.525 + (not self.inherited or not self.inherited.has_key(name)) and \
1.526 + self.have_name(name)
1.527 +
1.528 + def have_name(self, name):
1.529 +
1.530 + "Return whether 'name' is known, perhaps having been inherited."
1.531 +
1.532 + return self.attribute_branches[-1].get(name)
1.533 +
1.534 + def assign_names(self, names, values=None):
1.535 +
1.536 + """
1.537 + Define the start of usage tracking for the given 'names', each being
1.538 + assigned with the corresponding 'values' if indicated.
1.539 + """
1.540 +
1.541 + branches = self.attribute_branches[-1]
1.542 + branch = Branch(names, True, values)
1.543 +
1.544 + for name in names:
1.545 + self.disconnect_name(name)
1.546 +
1.547 + branches[name] = [branch]
1.548 + init_item(self.assignments, name, list)
1.549 + self.assignments[name].append(branch)
1.550 +
1.551 + return branch
1.552 +
1.553 + def use_attribute(self, name, attrname):
1.554 +
1.555 + """
1.556 + Indicate the use on the given 'name' of an attribute with the given
1.557 + 'attrname'.
1.558 +
1.559 + Return all branches that support 'name'.
1.560 + """
1.561 +
1.562 + branches = self.attribute_branches[-1]
1.563 +
1.564 + # Add the usage to all current branches.
1.565 +
1.566 + if branches.has_key(name):
1.567 + for branch in branches[name]:
1.568 + branch.set_usage(name, attrname)
1.569 + return branches[name]
1.570 + else:
1.571 + return None
1.572 +
1.573 + # Query methods.
1.574 +
1.575 + def get_assignment_positions_for_branches(self, name, branches, missing=True):
1.576 +
1.577 + """
1.578 + Return the positions of assignments involving the given 'name' affected
1.579 + by the given 'branches'. If 'missing' is set to a false value, branches
1.580 + with missing name details will be excluded instead of contributing the
1.581 + value None to the list of positions.
1.582 + """
1.583 +
1.584 + if not branches:
1.585 + return [None]
1.586 +
1.587 + positions = set()
1.588 + assignments = self.assignments[name]
1.589 +
1.590 + for assignment in self.get_assignments_for_branches(name, branches):
1.591 +
1.592 + # Use None to indicate a branch without assignment information.
1.593 +
1.594 + if missing and isinstance(assignment, MissingBranch):
1.595 + positions.add(None)
1.596 + else:
1.597 + pos = assignments.index(assignment)
1.598 + positions.add(pos)
1.599 +
1.600 + positions = list(positions)
1.601 + positions.sort()
1.602 + return positions
1.603 +
1.604 + def get_assignments_for_branches(self, name, branches, missing=True):
1.605 +
1.606 + """
1.607 + Return the origins of assignments involving the given 'name' affected
1.608 + by the given 'branches'. The origins are a list of branches where names
1.609 + are defined using assignments. If 'missing' is set to a false value,
1.610 + branches with missing name details are excluded.
1.611 + """
1.612 +
1.613 + all_branches = []
1.614 + assignments = self.assignments[name]
1.615 +
1.616 + # Obtain the assignments recorded for each branch.
1.617 +
1.618 + for branch in branches:
1.619 +
1.620 + # Find the branch representing the definition of some names in the
1.621 + # scope's assignments, making sure that the given name is involved.
1.622 +
1.623 + for assignment in branch.get_assignment_sources(name):
1.624 +
1.625 + # Capture branches without assignment information as well as
1.626 + # genuine assignment branches.
1.627 +
1.628 + if assignment in assignments or missing and isinstance(assignment, MissingBranch):
1.629 + all_branches.append(assignment)
1.630 +
1.631 + return all_branches
1.632 +
1.633 + def get_all_usage(self):
1.634 +
1.635 + """
1.636 + Convert usage observations from the tracker to a simple mapping of
1.637 + names to sets of attribute names.
1.638 + """
1.639 +
1.640 + d = {}
1.641 + for name, branches in self.assignments.items():
1.642 + d[name] = self.get_usage_from_branches_for_name(branches, name)
1.643 + return d
1.644 +
1.645 + def get_usage_from_branches_for_name(self, branches, name):
1.646 +
1.647 + """
1.648 + Convert usage observations from the 'branches' to a simple list of
1.649 + usage sets for the given 'name'.
1.650 + """
1.651 +
1.652 + l = []
1.653 + for branch in branches:
1.654 + l.append(branch.get_usage()[name])
1.655 + return l
1.656 +
1.657 + def get_all_values(self):
1.658 +
1.659 + "Return a mapping from names to lists of assigned values."
1.660 +
1.661 + d = {}
1.662 + for name, branches in self.assignments.items():
1.663 + d[name] = [branch.values.get(name) for branch in branches]
1.664 + return d
1.665 +
1.666 +# Special objects.
1.667 +
1.668 +class AbandonedDict(dict):
1.669 +
1.670 + "A dictionary representing mappings in an abandoned branch."
1.671 +
1.672 + def __repr__(self):
1.673 + return "AbandonedDict()"
1.674 +
1.675 +class MissingBranch(Branch):
1.676 +
1.677 + "A branch introduced during dictionary merging."
1.678 +
1.679 + def __repr__(self):
1.680 + return "MissingBranch(%r, %r)" % (self.usage.keys(),
1.681 + self.assignments and True or False)
1.682 +
1.683 +def make_missing(name):
1.684 +
1.685 + "Make a special branch indicating missing name information."
1.686 +
1.687 + return set([MissingBranch([name], True)])
1.688 +
1.689 +# Dictionary utilities.
1.690 +
1.691 +def merge_dicts(dicts, ignored=AbandonedDict, missing=None):
1.692 +
1.693 + """
1.694 + Merge the given 'dicts' mapping keys to sets of values.
1.695 +
1.696 + Where 'ignored' is specified, any dictionary of the given type is ignored.
1.697 + Where all dictionaries to be merged are of the given type, an instance of
1.698 + the type is returned as the merged dictionary.
1.699 +
1.700 + Where 'missing' is specified, it provides a callable that produces a set of
1.701 + suitable values for a given name.
1.702 + """
1.703 +
1.704 + new_dict = {}
1.705 + all_names = set()
1.706 +
1.707 + # Determine all known names.
1.708 +
1.709 + for old_dict in dicts:
1.710 + all_names.update(old_dict.keys())
1.711 +
1.712 + # Merge the dictionaries, looking for all known names in each one.
1.713 +
1.714 + have_dicts = False
1.715 +
1.716 + for old_dict in dicts:
1.717 +
1.718 + # Abandoned dictionaries should not contribute information.
1.719 +
1.720 + if isinstance(old_dict, ignored):
1.721 + continue
1.722 + else:
1.723 + have_dicts = True
1.724 +
1.725 + for name in all_names:
1.726 +
1.727 + # Find branches providing each name.
1.728 +
1.729 + if old_dict.has_key(name):
1.730 + values = old_dict[name]
1.731 +
1.732 + # Branches not providing names may indicate usage before assignment.
1.733 +
1.734 + elif missing:
1.735 + values = missing(name)
1.736 + else:
1.737 + continue
1.738 +
1.739 + # Initialise mappings in the resulting dictionary.
1.740 +
1.741 + if not new_dict.has_key(name):
1.742 + new_dict[name] = set(values)
1.743 + else:
1.744 + new_dict[name].update(values)
1.745 +
1.746 + # Where no dictionaries contributed, all branches were abandoned.
1.747 +
1.748 + if have_dicts:
1.749 + return new_dict
1.750 + else:
1.751 + return ignored()
1.752 +
1.753 +def deepen_dict(d):
1.754 +
1.755 + """
1.756 + Return a version of dictionary 'd' with its values converted to sets
1.757 + containing each original value as a single element in each new value.
1.758 + Original values are assumed to be sequences. Thus...
1.759 +
1.760 + {"self" : ("x", "y")}
1.761 +
1.762 + ...would become...
1.763 +
1.764 + {"self" : set([("x", "y")])}
1.765 +
1.766 + ...allowing other such values to be added to the set alongside the original
1.767 + value.
1.768 + """
1.769 +
1.770 + l = []
1.771 +
1.772 + for key, value in d.items():
1.773 +
1.774 + # Sort the attribute name details for stable comparisons.
1.775 +
1.776 + value = list(value)
1.777 + value.sort()
1.778 + l.append((key, set([tuple(value)])))
1.779 +
1.780 + return dict(l)
1.781 +
1.782 +def combine_sets(s1, s2):
1.783 +
1.784 + "Combine elements from sets 's1' and 's2'."
1.785 +
1.786 + if not s1:
1.787 + return s2
1.788 + elif not s2:
1.789 + return s1
1.790 +
1.791 + s = set()
1.792 +
1.793 + for i1 in s1:
1.794 + for i2 in s2:
1.795 +
1.796 + # Sort the attribute name details for stable comparisons.
1.797 +
1.798 + l = list(set(i1 + i2))
1.799 + l.sort()
1.800 + s.add(tuple(l))
1.801 +
1.802 + return s
1.803 +
1.804 +def combine_dicts(d1, d2, combine=combine_sets):
1.805 +
1.806 + """
1.807 + Combine dictionaries 'd1' and 'd2' such that the values for common keys
1.808 + are themselves combined in the result.
1.809 + """
1.810 +
1.811 + d = {}
1.812 +
1.813 + for key in d1.keys():
1.814 + if d2.has_key(key):
1.815 + d[key] = combine(d1[key], d2[key])
1.816 + else:
1.817 + d[key] = d1[key]
1.818 +
1.819 + return d
1.820 +
1.821 +# vim: tabstop=4 expandtab shiftwidth=4
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/common.py Tue Aug 30 16:51:10 2016 +0200
2.3 @@ -0,0 +1,1067 @@
2.4 +#!/usr/bin/env python
2.5 +
2.6 +"""
2.7 +Common functions.
2.8 +
2.9 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,
2.10 + 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
2.11 +
2.12 +This program is free software; you can redistribute it and/or modify it under
2.13 +the terms of the GNU General Public License as published by the Free Software
2.14 +Foundation; either version 3 of the License, or (at your option) any later
2.15 +version.
2.16 +
2.17 +This program is distributed in the hope that it will be useful, but WITHOUT
2.18 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
2.19 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
2.20 +details.
2.21 +
2.22 +You should have received a copy of the GNU General Public License along with
2.23 +this program. If not, see <http://www.gnu.org/licenses/>.
2.24 +"""
2.25 +
2.26 +from errors import *
2.27 +from os import listdir, makedirs, remove
2.28 +from os.path import exists, isdir, join, split
2.29 +import compiler
2.30 +
2.31 +class CommonOutput:
2.32 +
2.33 + "Common output functionality."
2.34 +
2.35 + def check_output(self):
2.36 +
2.37 + "Check the existing output and remove it if irrelevant."
2.38 +
2.39 + if not exists(self.output):
2.40 + makedirs(self.output)
2.41 +
2.42 + details = self.importer.get_cache_details()
2.43 + recorded_details = self.get_output_details()
2.44 +
2.45 + if recorded_details != details:
2.46 + self.remove_output()
2.47 +
2.48 + writefile(self.get_output_details_filename(), details)
2.49 +
2.50 + def get_output_details_filename(self):
2.51 +
2.52 + "Return the output details filename."
2.53 +
2.54 + return join(self.output, "$details")
2.55 +
2.56 + def get_output_details(self):
2.57 +
2.58 + "Return details of the existing output."
2.59 +
2.60 + details_filename = self.get_output_details_filename()
2.61 +
2.62 + if not exists(details_filename):
2.63 + return None
2.64 + else:
2.65 + return readfile(details_filename)
2.66 +
2.67 + def remove_output(self, dirname=None):
2.68 +
2.69 + "Remove the output."
2.70 +
2.71 + dirname = dirname or self.output
2.72 +
2.73 + for filename in listdir(dirname):
2.74 + path = join(dirname, filename)
2.75 + if isdir(path):
2.76 + self.remove_output(path)
2.77 + else:
2.78 + remove(path)
2.79 +
2.80 +class CommonModule:
2.81 +
2.82 + "A common module representation."
2.83 +
2.84 + def __init__(self, name, importer):
2.85 +
2.86 + """
2.87 + Initialise this module with the given 'name' and an 'importer' which is
2.88 + used to provide access to other modules when required.
2.89 + """
2.90 +
2.91 + self.name = name
2.92 + self.importer = importer
2.93 + self.filename = None
2.94 +
2.95 + # Inspection-related attributes.
2.96 +
2.97 + self.astnode = None
2.98 + self.iterators = {}
2.99 + self.temp = {}
2.100 + self.lambdas = {}
2.101 +
2.102 + # Constants, literals and values.
2.103 +
2.104 + self.constants = {}
2.105 + self.constant_values = {}
2.106 + self.literals = {}
2.107 + self.literal_types = {}
2.108 +
2.109 + # Nested namespaces.
2.110 +
2.111 + self.namespace_path = []
2.112 + self.in_function = False
2.113 +
2.114 + # Attribute chains.
2.115 +
2.116 + self.attrs = []
2.117 +
2.118 + def __repr__(self):
2.119 + return "CommonModule(%r, %r)" % (self.name, self.importer)
2.120 +
2.121 + def parse_file(self, filename):
2.122 +
2.123 + "Parse the file with the given 'filename', initialising attributes."
2.124 +
2.125 + self.filename = filename
2.126 + self.astnode = compiler.parseFile(filename)
2.127 +
2.128 + # Module-relative naming.
2.129 +
2.130 + def get_global_path(self, name):
2.131 + return "%s.%s" % (self.name, name)
2.132 +
2.133 + def get_namespace_path(self):
2.134 + return ".".join([self.name] + self.namespace_path)
2.135 +
2.136 + def get_object_path(self, name):
2.137 + return ".".join([self.name] + self.namespace_path + [name])
2.138 +
2.139 + def get_parent_path(self):
2.140 + return ".".join([self.name] + self.namespace_path[:-1])
2.141 +
2.142 + # Namespace management.
2.143 +
2.144 + def enter_namespace(self, name):
2.145 +
2.146 + "Enter the namespace having the given 'name'."
2.147 +
2.148 + self.namespace_path.append(name)
2.149 +
2.150 + def exit_namespace(self):
2.151 +
2.152 + "Exit the current namespace."
2.153 +
2.154 + self.namespace_path.pop()
2.155 +
2.156 + # Constant reference naming.
2.157 +
2.158 + def get_constant_name(self, value):
2.159 +
2.160 + "Add a new constant to the current namespace for 'value'."
2.161 +
2.162 + path = self.get_namespace_path()
2.163 + init_item(self.constants, path, dict)
2.164 + return "$c%d" % add_counter_item(self.constants[path], value)
2.165 +
2.166 + # Literal reference naming.
2.167 +
2.168 + def get_literal_name(self):
2.169 +
2.170 + "Add a new literal to the current namespace."
2.171 +
2.172 + path = self.get_namespace_path()
2.173 + init_item(self.literals, path, lambda: 0)
2.174 + return "$C%d" % self.literals[path]
2.175 +
2.176 + def next_literal(self):
2.177 + self.literals[self.get_namespace_path()] += 1
2.178 +
2.179 + # Temporary iterator naming.
2.180 +
2.181 + def get_iterator_path(self):
2.182 + return self.in_function and self.get_namespace_path() or self.name
2.183 +
2.184 + def get_iterator_name(self):
2.185 + path = self.get_iterator_path()
2.186 + init_item(self.iterators, path, lambda: 0)
2.187 + return "$i%d" % self.iterators[path]
2.188 +
2.189 + def next_iterator(self):
2.190 + self.iterators[self.get_iterator_path()] += 1
2.191 +
2.192 + # Temporary variable naming.
2.193 +
2.194 + def get_temporary_name(self):
2.195 + path = self.get_namespace_path()
2.196 + init_item(self.temp, path, lambda: 0)
2.197 + return "$t%d" % self.temp[path]
2.198 +
2.199 + def next_temporary(self):
2.200 + self.temp[self.get_namespace_path()] += 1
2.201 +
2.202 + # Arbitrary function naming.
2.203 +
2.204 + def get_lambda_name(self):
2.205 + path = self.get_namespace_path()
2.206 + init_item(self.lambdas, path, lambda: 0)
2.207 + name = "$l%d" % self.lambdas[path]
2.208 + self.lambdas[path] += 1
2.209 + return name
2.210 +
2.211 + def reset_lambdas(self):
2.212 + self.lambdas = {}
2.213 +
2.214 + # Constant and literal recording.
2.215 +
2.216 + def get_constant_reference(self, ref, value):
2.217 +
2.218 + "Return a constant reference for the given 'ref' type and 'value'."
2.219 +
2.220 + constant_name = self.get_constant_name(value)
2.221 +
2.222 + # Return a reference for the constant.
2.223 +
2.224 + objpath = self.get_object_path(constant_name)
2.225 + name_ref = ConstantValueRef(constant_name, ref.instance_of(), value)
2.226 +
2.227 + # Record the value and type for the constant.
2.228 +
2.229 + self.constant_values[objpath] = name_ref.value, name_ref.get_origin()
2.230 + return name_ref
2.231 +
2.232 + def get_literal_reference(self, name, ref, items, cls):
2.233 +
2.234 + # Construct an invocation using the items as arguments.
2.235 +
2.236 + typename = "$L%s" % name
2.237 +
2.238 + invocation = compiler.ast.CallFunc(
2.239 + compiler.ast.Name(typename),
2.240 + items
2.241 + )
2.242 +
2.243 + # Get a name for the actual literal.
2.244 +
2.245 + instname = self.get_literal_name()
2.246 + self.next_literal()
2.247 +
2.248 + # Record the type for the literal.
2.249 +
2.250 + objpath = self.get_object_path(instname)
2.251 + self.literal_types[objpath] = ref.get_origin()
2.252 +
2.253 + # Return a wrapper for the invocation exposing the items.
2.254 +
2.255 + return cls(
2.256 + instname,
2.257 + ref.instance_of(),
2.258 + self.process_structure_node(invocation),
2.259 + invocation.args
2.260 + )
2.261 +
2.262 + # Node handling.
2.263 +
2.264 + def process_structure(self, node):
2.265 +
2.266 + """
2.267 + Within the given 'node', process the program structure.
2.268 +
2.269 + During inspection, this will process global declarations, adjusting the
2.270 + module namespace, and import statements, building a module dependency
2.271 + hierarchy.
2.272 +
2.273 + During translation, this will consult deduced program information and
2.274 + output translated code.
2.275 + """
2.276 +
2.277 + l = []
2.278 + for n in node.getChildNodes():
2.279 + l.append(self.process_structure_node(n))
2.280 + return l
2.281 +
2.282 + def process_augassign_node(self, n):
2.283 +
2.284 + "Process the given augmented assignment node 'n'."
2.285 +
2.286 + op = operator_functions[n.op]
2.287 +
2.288 + if isinstance(n.node, compiler.ast.Getattr):
2.289 + target = compiler.ast.AssAttr(n.node.expr, n.node.attrname, "OP_ASSIGN")
2.290 + elif isinstance(n.node, compiler.ast.Name):
2.291 + target = compiler.ast.AssName(n.node.name, "OP_ASSIGN")
2.292 + else:
2.293 + target = n.node
2.294 +
2.295 + assignment = compiler.ast.Assign(
2.296 + [target],
2.297 + compiler.ast.CallFunc(
2.298 + compiler.ast.Name("$op%s" % op),
2.299 + [n.node, n.expr]))
2.300 +
2.301 + return self.process_structure_node(assignment)
2.302 +
2.303 + def process_assignment_for_function(self, original_name, name):
2.304 +
2.305 + """
2.306 + Return an assignment operation making 'original_name' refer to the given
2.307 + 'name'.
2.308 + """
2.309 +
2.310 + assignment = compiler.ast.Assign(
2.311 + [compiler.ast.AssName(original_name, "OP_ASSIGN")],
2.312 + compiler.ast.Name(name)
2.313 + )
2.314 +
2.315 + return self.process_structure_node(assignment)
2.316 +
2.317 + def process_assignment_node_items(self, n, expr):
2.318 +
2.319 + """
2.320 + Process the given assignment node 'n' whose children are to be assigned
2.321 + items of 'expr'.
2.322 + """
2.323 +
2.324 + name_ref = self.process_structure_node(expr)
2.325 +
2.326 + # Either unpack the items and present them directly to each assignment
2.327 + # node.
2.328 +
2.329 + if isinstance(name_ref, LiteralSequenceRef):
2.330 + self.process_literal_sequence_items(n, name_ref)
2.331 +
2.332 + # Or have the assignment nodes access each item via the sequence API.
2.333 +
2.334 + else:
2.335 + self.process_assignment_node_items_by_position(n, expr, name_ref)
2.336 +
2.337 + def process_assignment_node_items_by_position(self, n, expr, name_ref):
2.338 +
2.339 + """
2.340 + Process the given sequence assignment node 'n', converting the node to
2.341 + the separate assignment of each target using positional access on a
2.342 + temporary variable representing the sequence. Use 'expr' as the assigned
2.343 + value and 'name_ref' as the reference providing any existing temporary
2.344 + variable.
2.345 + """
2.346 +
2.347 + assignments = []
2.348 +
2.349 + if isinstance(name_ref, NameRef):
2.350 + temp = name_ref.name
2.351 + else:
2.352 + temp = self.get_temporary_name()
2.353 + self.next_temporary()
2.354 +
2.355 + assignments.append(
2.356 + compiler.ast.Assign([compiler.ast.AssName(temp, "OP_ASSIGN")], expr)
2.357 + )
2.358 +
2.359 + for i, node in enumerate(n.nodes):
2.360 + assignments.append(
2.361 + compiler.ast.Assign([node], compiler.ast.Subscript(
2.362 + compiler.ast.Name(temp), "OP_APPLY", [compiler.ast.Const(i)]))
2.363 + )
2.364 +
2.365 + return self.process_structure_node(compiler.ast.Stmt(assignments))
2.366 +
2.367 + def process_literal_sequence_items(self, n, name_ref):
2.368 +
2.369 + """
2.370 + Process the given assignment node 'n', obtaining from the given
2.371 + 'name_ref' the items to be assigned to the assignment targets.
2.372 + """
2.373 +
2.374 + if len(n.nodes) == len(name_ref.items):
2.375 + for node, item in zip(n.nodes, name_ref.items):
2.376 + self.process_assignment_node(node, item)
2.377 + else:
2.378 + raise InspectError("In %s, item assignment needing %d items is given %d items." % (
2.379 + self.get_namespace_path(), len(n.nodes), len(name_ref.items)))
2.380 +
2.381 + def process_compare_node(self, n):
2.382 +
2.383 + """
2.384 + Process the given comparison node 'n', converting an operator sequence
2.385 + from...
2.386 +
2.387 + <expr1> <op1> <expr2> <op2> <expr3>
2.388 +
2.389 + ...to...
2.390 +
2.391 + <op1>(<expr1>, <expr2>) and <op2>(<expr2>, <expr3>)
2.392 + """
2.393 +
2.394 + invocations = []
2.395 + last = n.expr
2.396 +
2.397 + for op, op_node in n.ops:
2.398 + op = operator_functions.get(op)
2.399 +
2.400 + invocations.append(compiler.ast.CallFunc(
2.401 + compiler.ast.Name("$op%s" % op),
2.402 + [last, op_node]))
2.403 +
2.404 + last = op_node
2.405 +
2.406 + if len(invocations) > 1:
2.407 + result = compiler.ast.And(invocations)
2.408 + else:
2.409 + result = invocations[0]
2.410 +
2.411 + return self.process_structure_node(result)
2.412 +
2.413 + def process_dict_node(self, node):
2.414 +
2.415 + """
2.416 + Process the given dictionary 'node', returning a list of (key, value)
2.417 + tuples.
2.418 + """
2.419 +
2.420 + l = []
2.421 + for key, value in node.items:
2.422 + l.append((
2.423 + self.process_structure_node(key),
2.424 + self.process_structure_node(value)))
2.425 + return l
2.426 +
2.427 + def process_for_node(self, n):
2.428 +
2.429 + """
2.430 + Generate attribute accesses for {n.list}.__iter__ and the next method on
2.431 + the iterator, producing a replacement node for the original.
2.432 + """
2.433 +
2.434 + node = compiler.ast.Stmt([
2.435 +
2.436 + # <iterator> = {n.list}.__iter__
2.437 +
2.438 + compiler.ast.Assign(
2.439 + [compiler.ast.AssName(self.get_iterator_name(), "OP_ASSIGN")],
2.440 + compiler.ast.CallFunc(
2.441 + compiler.ast.Getattr(n.list, "__iter__"),
2.442 + []
2.443 + )),
2.444 +
2.445 + # try:
2.446 + # while True:
2.447 + # <var>... = <iterator>.next()
2.448 + # ...
2.449 + # except StopIteration:
2.450 + # pass
2.451 +
2.452 + compiler.ast.TryExcept(
2.453 + compiler.ast.While(
2.454 + compiler.ast.Name("True"),
2.455 + compiler.ast.Stmt([
2.456 + compiler.ast.Assign(
2.457 + [n.assign],
2.458 + compiler.ast.CallFunc(
2.459 + compiler.ast.Getattr(compiler.ast.Name(self.get_iterator_name()), "next"),
2.460 + []
2.461 + )),
2.462 + n.body]),
2.463 + None),
2.464 + [(compiler.ast.Name("StopIteration"), None, compiler.ast.Stmt([compiler.ast.Pass()]))],
2.465 + None)
2.466 + ])
2.467 +
2.468 + self.next_iterator()
2.469 + self.process_structure_node(node)
2.470 +
2.471 + def convert_ifexp_node(self, n):
2.472 +
2.473 + """
2.474 + Convert the given if expression node 'n'. An if expression is considered
2.475 + as mapping to a function body containing an if statement as follows:
2.476 +
2.477 + <expr> if <test> else <altexpr>
2.478 +
2.479 + lambda <argnames>:
2.480 + if <test>:
2.481 + return <expr>
2.482 + else:
2.483 + return <altexpr>
2.484 +
2.485 + The <argnames> are populated with defaults after the node has been
2.486 + processed.
2.487 + """
2.488 +
2.489 + node = compiler.ast.Lambda(
2.490 + [], [], 0,
2.491 + compiler.ast.If([
2.492 + (n.test, compiler.ast.Return(n.then))
2.493 + ],
2.494 + compiler.ast.Return(n.else_)
2.495 + ))
2.496 +
2.497 + return node
2.498 +
2.499 + def convert_listcomp_node(self, n):
2.500 +
2.501 + """
2.502 + Convert the given list comprehension node 'n'. A list comprehension is
2.503 + considered as mapping to a function body containing a for loop as
2.504 + follows:
2.505 +
2.506 + [<expr> for <varexpr1> in <list1> if <ifexpr1> for <varexpr2> in <list2> if <ifexpr2> if <ifexpr3>]
2.507 +
2.508 + lambda <argnames>:
2.509 + <result> = []
2.510 + for <varexpr1> in <list1>:
2.511 + if <ifexpr1>:
2.512 + for <varexpr2> in <list2>:
2.513 + if <ifexpr2>:
2.514 + if <ifexpr3>:
2.515 + <result>.append(<expr>)
2.516 + return <result>
2.517 +
2.518 + The <argnames> are populated with defaults after the node has been
2.519 + processed.
2.520 + """
2.521 +
2.522 + temp = "$tr"
2.523 +
2.524 + node = compiler.ast.Lambda(
2.525 + [], [], 0,
2.526 + compiler.ast.Stmt([
2.527 +
2.528 + # <result> = []
2.529 +
2.530 + compiler.ast.Assign([compiler.ast.AssName(temp, "OP_ASSIGN")],
2.531 + compiler.ast.List([])
2.532 + ),
2.533 +
2.534 + # for ...
2.535 +
2.536 + self.convert_listcomp_for_node(n.quals[0], n.quals[1:], n.expr, temp),
2.537 +
2.538 + # return <result>
2.539 +
2.540 + compiler.ast.Return(compiler.ast.Name(temp))
2.541 + ]))
2.542 +
2.543 + return node
2.544 +
2.545 + def convert_listcomp_for_node(self, loop, following_loops, expr, temp):
2.546 +
2.547 + """
2.548 + Return a node representing 'loop', encapsulating 'following_loops' and
2.549 + employing 'expr' in the innermost loop body appending to 'temp'.
2.550 + """
2.551 +
2.552 + if loop.ifs:
2.553 + body = self.convert_listcomp_if_node(loop.ifs[0], loop.ifs[1:], following_loops, expr, temp)
2.554 + elif following_loops:
2.555 + body = self.convert_listcomp_for_node(following_loops[0], following_loops[1:], expr, temp)
2.556 + else:
2.557 + body = self.convert_listcomp_body_node(expr, temp)
2.558 +
2.559 + return compiler.ast.For(loop.assign, loop.list, compiler.ast.Stmt([body]), None)
2.560 +
2.561 + def convert_listcomp_if_node(self, if_, following_ifs, following_loops, expr, temp):
2.562 +
2.563 + """
2.564 + Return a node representing 'if_', encapsulating the 'following_ifs' and
2.565 + 'following_loops' and employing 'expr' in the innermost loop body
2.566 + appending to 'temp'.
2.567 + """
2.568 +
2.569 + if following_ifs:
2.570 + body = self.convert_listcomp_if_node(following_ifs[0], following_ifs[1:], following_loops, expr, temp)
2.571 + elif following_loops:
2.572 + body = self.convert_listcomp_for_node(following_loops[0], following_loops[1:], expr, temp)
2.573 + else:
2.574 + body = self.convert_listcomp_body_node(expr, temp)
2.575 +
2.576 + return compiler.ast.If([(if_.test, compiler.ast.Stmt([body]))], None)
2.577 +
2.578 + def convert_listcomp_body_node(self, expr, temp):
2.579 +
2.580 + "Return a node appending 'expr' to 'temp'."
2.581 +
2.582 + return compiler.ast.Discard(
2.583 + compiler.ast.CallFunc(
2.584 + compiler.ast.Getattr(compiler.ast.Name(temp), "append"), [expr]))
2.585 +
2.586 + def process_literal_sequence_node(self, n, name, ref, cls):
2.587 +
2.588 + """
2.589 + Process the given literal sequence node 'n' as a function invocation,
2.590 + with 'name' indicating the type of the sequence, and 'ref' being a
2.591 + reference to the type. The 'cls' is used to instantiate a suitable name
2.592 + reference.
2.593 + """
2.594 +
2.595 + if name == "dict":
2.596 + items = []
2.597 + for key, value in n.items:
2.598 + items.append(compiler.ast.Tuple([key, value]))
2.599 + else: # name in ("list", "tuple"):
2.600 + items = n.nodes
2.601 +
2.602 + return self.get_literal_reference(name, ref, items, cls)
2.603 +
2.604 + def process_operator_node(self, n):
2.605 +
2.606 + """
2.607 + Process the given operator node 'n' as an operator function invocation.
2.608 + """
2.609 +
2.610 + op = operator_functions[n.__class__.__name__]
2.611 + invocation = compiler.ast.CallFunc(
2.612 + compiler.ast.Name("$op%s" % op),
2.613 + list(n.getChildNodes())
2.614 + )
2.615 + return self.process_structure_node(invocation)
2.616 +
2.617 + def process_slice_node(self, n, expr=None):
2.618 +
2.619 + """
2.620 + Process the given slice node 'n' as an operator function invocation.
2.621 + """
2.622 +
2.623 + op = n.flags == "OP_ASSIGN" and "setslice" or "getslice"
2.624 + invocation = compiler.ast.CallFunc(
2.625 + compiler.ast.Name("$op%s" % op),
2.626 + [n.expr, n.lower or compiler.ast.Name("None"), n.upper or compiler.ast.Name("None")] +
2.627 + (expr and [expr] or [])
2.628 + )
2.629 + return self.process_structure_node(invocation)
2.630 +
2.631 + def process_sliceobj_node(self, n):
2.632 +
2.633 + """
2.634 + Process the given slice object node 'n' as a slice constructor.
2.635 + """
2.636 +
2.637 + op = "slice"
2.638 + invocation = compiler.ast.CallFunc(
2.639 + compiler.ast.Name("$op%s" % op),
2.640 + n.nodes
2.641 + )
2.642 + return self.process_structure_node(invocation)
2.643 +
2.644 + def process_subscript_node(self, n, expr=None):
2.645 +
2.646 + """
2.647 + Process the given subscript node 'n' as an operator function invocation.
2.648 + """
2.649 +
2.650 + op = n.flags == "OP_ASSIGN" and "setitem" or "getitem"
2.651 + invocation = compiler.ast.CallFunc(
2.652 + compiler.ast.Name("$op%s" % op),
2.653 + [n.expr] + list(n.subs) + (expr and [expr] or [])
2.654 + )
2.655 + return self.process_structure_node(invocation)
2.656 +
2.657 + def process_attribute_chain(self, n):
2.658 +
2.659 + """
2.660 + Process the given attribute access node 'n'. Return a reference
2.661 + describing the expression.
2.662 + """
2.663 +
2.664 + # AssAttr/Getattr are nested with the outermost access being the last
2.665 + # access in any chain.
2.666 +
2.667 + self.attrs.insert(0, n.attrname)
2.668 + attrs = self.attrs
2.669 +
2.670 + # Break attribute chains where non-access nodes are found.
2.671 +
2.672 + if not self.have_access_expression(n):
2.673 + self.attrs = []
2.674 +
2.675 + # Descend into the expression, extending backwards any existing chain,
2.676 + # or building another for the expression.
2.677 +
2.678 + name_ref = self.process_structure_node(n.expr)
2.679 +
2.680 + # Restore chain information applying to this node.
2.681 +
2.682 + self.attrs = attrs
2.683 +
2.684 + # Return immediately if the expression was another access and thus a
2.685 + # continuation backwards along the chain. The above processing will
2.686 + # have followed the chain all the way to its conclusion.
2.687 +
2.688 + if self.have_access_expression(n):
2.689 + del self.attrs[0]
2.690 +
2.691 + return name_ref
2.692 +
2.693 + def have_access_expression(self, node):
2.694 +
2.695 + "Return whether the expression associated with 'node' is Getattr."
2.696 +
2.697 + return isinstance(node.expr, compiler.ast.Getattr)
2.698 +
2.699 + def get_name_for_tracking(self, name, path=None):
2.700 +
2.701 + """
2.702 + Return the name to be used for attribute usage observations involving
2.703 + the given 'name' in the current namespace. If 'path' is indicated and
2.704 + the name is being used outside a function, return the path value;
2.705 + otherwise, return a path computed using the current namespace and the
2.706 + given name.
2.707 +
2.708 + The intention of this method is to provide a suitably-qualified name
2.709 + that can be tracked across namespaces. Where globals are being
2.710 + referenced in class namespaces, they should be referenced using their
2.711 + path within the module, not using a path within each class.
2.712 +
2.713 + It may not be possible to identify a global within a function at the
2.714 + time of inspection (since a global may appear later in a file).
2.715 + Consequently, globals are identified by their local name rather than
2.716 + their module-qualified path.
2.717 + """
2.718 +
2.719 + # For functions, use the appropriate local names.
2.720 +
2.721 + if self.in_function:
2.722 + return name
2.723 +
2.724 + # For static namespaces, use the given qualified name.
2.725 +
2.726 + elif path:
2.727 + return path
2.728 +
2.729 + # Otherwise, establish a name in the current (module) namespace.
2.730 +
2.731 + else:
2.732 + return self.get_object_path(name)
2.733 +
2.734 + def get_path_for_access(self):
2.735 +
2.736 + "Outside functions, register accesses at the module level."
2.737 +
2.738 + if not self.in_function:
2.739 + return self.name
2.740 + else:
2.741 + return self.get_namespace_path()
2.742 +
2.743 + def get_module_name(self, node):
2.744 +
2.745 + """
2.746 + Using the given From 'node' in this module, calculate any relative import
2.747 + information, returning a tuple containing a module to import along with any
2.748 + names to import based on the node's name information.
2.749 +
2.750 + Where the returned module is given as None, whole module imports should
2.751 + be performed for the returned modules using the returned names.
2.752 + """
2.753 +
2.754 + # Absolute import.
2.755 +
2.756 + if node.level == 0:
2.757 + return node.modname, node.names
2.758 +
2.759 + # Relative to an ancestor of this module.
2.760 +
2.761 + else:
2.762 + path = self.name.split(".")
2.763 + level = node.level
2.764 +
2.765 + # Relative imports treat package roots as submodules.
2.766 +
2.767 + if split(self.filename)[-1] == "__init__.py":
2.768 + level -= 1
2.769 +
2.770 + if level > len(path):
2.771 + raise InspectError("Relative import %r involves too many levels up from module %r" % (
2.772 + ("%s%s" % ("." * node.level, node.modname or "")), self.name))
2.773 +
2.774 + basename = ".".join(path[:len(path)-level])
2.775 +
2.776 + # Name imports from a module.
2.777 +
2.778 + if node.modname:
2.779 + return "%s.%s" % (basename, node.modname), node.names
2.780 +
2.781 + # Relative whole module imports.
2.782 +
2.783 + else:
2.784 + return basename, node.names
2.785 +
2.786 +def get_argnames(args):
2.787 +
2.788 + """
2.789 + Return a list of all names provided by 'args'. Since tuples may be
2.790 + employed, the arguments are traversed depth-first.
2.791 + """
2.792 +
2.793 + l = []
2.794 + for arg in args:
2.795 + if isinstance(arg, tuple):
2.796 + l += get_argnames(arg)
2.797 + else:
2.798 + l.append(arg)
2.799 + return l
2.800 +
2.801 +# Classes representing inspection and translation observations.
2.802 +
2.803 +class Result:
2.804 +
2.805 + "An abstract expression result."
2.806 +
2.807 + def is_name(self):
2.808 + return False
2.809 + def get_origin(self):
2.810 + return None
2.811 +
2.812 +class NameRef(Result):
2.813 +
2.814 + "A reference to a name."
2.815 +
2.816 + def __init__(self, name, expr=None):
2.817 + self.name = name
2.818 + self.expr = expr
2.819 +
2.820 + def is_name(self):
2.821 + return True
2.822 +
2.823 + def reference(self):
2.824 + return None
2.825 +
2.826 + def final(self):
2.827 + return None
2.828 +
2.829 + def __repr__(self):
2.830 + return "NameRef(%r, %r)" % (self.name, self.expr)
2.831 +
2.832 +class LocalNameRef(NameRef):
2.833 +
2.834 + "A reference to a local name."
2.835 +
2.836 + def __init__(self, name, number):
2.837 + NameRef.__init__(self, name)
2.838 + self.number = number
2.839 +
2.840 + def __repr__(self):
2.841 + return "LocalNameRef(%r, %r)" % (self.name, self.number)
2.842 +
2.843 +class ResolvedNameRef(NameRef):
2.844 +
2.845 + "A resolved name-based reference."
2.846 +
2.847 + def __init__(self, name, ref, expr=None):
2.848 + NameRef.__init__(self, name, expr)
2.849 + self.ref = ref
2.850 +
2.851 + def reference(self):
2.852 + return self.ref
2.853 +
2.854 + def get_name(self):
2.855 + return self.ref and self.ref.get_name() or None
2.856 +
2.857 + def get_origin(self):
2.858 + return self.ref and self.ref.get_origin() or None
2.859 +
2.860 + def static(self):
2.861 + return self.ref and self.ref.static() or None
2.862 +
2.863 + def final(self):
2.864 + return self.ref and self.ref.final() or None
2.865 +
2.866 + def has_kind(self, kinds):
2.867 + return self.ref and self.ref.has_kind(kinds)
2.868 +
2.869 + def __repr__(self):
2.870 + return "ResolvedNameRef(%r, %r, %r)" % (self.name, self.ref, self.expr)
2.871 +
2.872 +class ConstantValueRef(ResolvedNameRef):
2.873 +
2.874 + "A constant reference representing a single literal value."
2.875 +
2.876 + def __init__(self, name, ref, value, number=None):
2.877 + ResolvedNameRef.__init__(self, name, ref)
2.878 + self.value = value
2.879 + self.number = number
2.880 +
2.881 + def __repr__(self):
2.882 + return "ConstantValueRef(%r, %r, %r, %r)" % (self.name, self.ref, self.value, self.number)
2.883 +
2.884 +class InstanceRef(Result):
2.885 +
2.886 + "An instance reference."
2.887 +
2.888 + def __init__(self, ref):
2.889 + self.ref = ref
2.890 +
2.891 + def reference(self):
2.892 + return self.ref
2.893 +
2.894 + def __repr__(self):
2.895 + return "InstanceRef(%r)" % self.ref
2.896 +
2.897 +class LiteralSequenceRef(ResolvedNameRef):
2.898 +
2.899 + "A reference representing a sequence of values."
2.900 +
2.901 + def __init__(self, name, ref, node, items=None):
2.902 + ResolvedNameRef.__init__(self, name, ref)
2.903 + self.node = node
2.904 + self.items = items
2.905 +
2.906 + def __repr__(self):
2.907 + return "LiteralSequenceRef(%r, %r, %r, %r)" % (self.name, self.ref, self.node, self.items)
2.908 +
2.909 +# Dictionary utilities.
2.910 +
2.911 +def init_item(d, key, fn):
2.912 +
2.913 + """
2.914 + Add to 'd' an entry for 'key' using the callable 'fn' to make an initial
2.915 + value where no entry already exists.
2.916 + """
2.917 +
2.918 + if not d.has_key(key):
2.919 + d[key] = fn()
2.920 + return d[key]
2.921 +
2.922 +def dict_for_keys(d, keys):
2.923 +
2.924 + "Return a new dictionary containing entries from 'd' for the given 'keys'."
2.925 +
2.926 + nd = {}
2.927 + for key in keys:
2.928 + if d.has_key(key):
2.929 + nd[key] = d[key]
2.930 + return nd
2.931 +
2.932 +def make_key(s):
2.933 +
2.934 + "Make sequence 's' into a tuple-based key, first sorting its contents."
2.935 +
2.936 + l = list(s)
2.937 + l.sort()
2.938 + return tuple(l)
2.939 +
2.940 +def add_counter_item(d, key):
2.941 +
2.942 + """
2.943 + Make a mapping in 'd' for 'key' to the number of keys added before it, thus
2.944 + maintaining a mapping of keys to their order of insertion.
2.945 + """
2.946 +
2.947 + if not d.has_key(key):
2.948 + d[key] = len(d.keys())
2.949 + return d[key]
2.950 +
2.951 +def remove_items(d1, d2):
2.952 +
2.953 + "Remove from 'd1' all items from 'd2'."
2.954 +
2.955 + for key in d2.keys():
2.956 + if d1.has_key(key):
2.957 + del d1[key]
2.958 +
2.959 +# Set utilities.
2.960 +
2.961 +def first(s):
2.962 + return list(s)[0]
2.963 +
2.964 +def same(s1, s2):
2.965 + return set(s1) == set(s2)
2.966 +
2.967 +# General input/output.
2.968 +
2.969 +def readfile(filename):
2.970 +
2.971 + "Return the contents of 'filename'."
2.972 +
2.973 + f = open(filename)
2.974 + try:
2.975 + return f.read()
2.976 + finally:
2.977 + f.close()
2.978 +
2.979 +def writefile(filename, s):
2.980 +
2.981 + "Write to 'filename' the string 's'."
2.982 +
2.983 + f = open(filename, "w")
2.984 + try:
2.985 + f.write(s)
2.986 + finally:
2.987 + f.close()
2.988 +
2.989 +# General encoding.
2.990 +
2.991 +def sorted_output(x):
2.992 +
2.993 + "Sort sequence 'x' and return a string with commas separating the values."
2.994 +
2.995 + x = map(str, x)
2.996 + x.sort()
2.997 + return ", ".join(x)
2.998 +
2.999 +# Attribute chain decoding.
2.1000 +
2.1001 +def get_attrnames(attrnames):
2.1002 + if attrnames.startswith("#"):
2.1003 + return [attrnames]
2.1004 + else:
2.1005 + return attrnames.split(".")
2.1006 +
2.1007 +def get_attrname_from_location(location):
2.1008 + path, name, attrnames, access = location
2.1009 + return get_attrnames(attrnames)[0]
2.1010 +
2.1011 +# Useful data.
2.1012 +
2.1013 +predefined_constants = "Ellipsis", "False", "None", "NotImplemented", "True"
2.1014 +
2.1015 +operator_functions = {
2.1016 +
2.1017 + # Fundamental operations.
2.1018 +
2.1019 + "is" : "is_",
2.1020 + "is not" : "is_not",
2.1021 +
2.1022 + # Binary operations.
2.1023 +
2.1024 + "in" : "in_",
2.1025 + "not in" : "not_in",
2.1026 + "Add" : "add",
2.1027 + "Bitand" : "and_",
2.1028 + "Bitor" : "or_",
2.1029 + "Bitxor" : "xor",
2.1030 + "Div" : "div",
2.1031 + "FloorDiv" : "floordiv",
2.1032 + "LeftShift" : "lshift",
2.1033 + "Mod" : "mod",
2.1034 + "Mul" : "mul",
2.1035 + "Power" : "pow",
2.1036 + "RightShift" : "rshift",
2.1037 + "Sub" : "sub",
2.1038 +
2.1039 + # Unary operations.
2.1040 +
2.1041 + "Invert" : "invert",
2.1042 + "UnaryAdd" : "pos",
2.1043 + "UnarySub" : "neg",
2.1044 +
2.1045 + # Augmented assignment.
2.1046 +
2.1047 + "+=" : "iadd",
2.1048 + "-=" : "isub",
2.1049 + "*=" : "imul",
2.1050 + "/=" : "idiv",
2.1051 + "//=" : "ifloordiv",
2.1052 + "%=" : "imod",
2.1053 + "**=" : "ipow",
2.1054 + "<<=" : "ilshift",
2.1055 + ">>=" : "irshift",
2.1056 + "&=" : "iand",
2.1057 + "^=" : "ixor",
2.1058 + "|=" : "ior",
2.1059 +
2.1060 + # Comparisons.
2.1061 +
2.1062 + "==" : "eq",
2.1063 + "!=" : "ne",
2.1064 + "<" : "lt",
2.1065 + "<=" : "le",
2.1066 + ">=" : "ge",
2.1067 + ">" : "gt",
2.1068 + }
2.1069 +
2.1070 +# vim: tabstop=4 expandtab shiftwidth=4
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/compiler/__init__.py Tue Aug 30 16:51:10 2016 +0200
3.3 @@ -0,0 +1,14 @@
3.4 +"""Package for parsing and compiling Python source code
3.5 +
3.6 +There are several functions defined at the top level that are imported
3.7 +from modules contained in the package.
3.8 +
3.9 +parse(buf, mode="exec") -> AST
3.10 + Converts a string containing Python source code to an abstract
3.11 + syntax tree (AST). The AST is defined in compiler.ast.
3.12 +
3.13 +parseFile(path) -> AST
3.14 + The same as parse(open(path))
3.15 +"""
3.16 +
3.17 +from compiler.transformer import parse, parseFile
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/compiler/ast.py Tue Aug 30 16:51:10 2016 +0200
4.3 @@ -0,0 +1,1786 @@
4.4 +"""Python abstract syntax node definitions
4.5 +
4.6 +This file was originally generated by Tools/compiler/astgen.py
4.7 +"""
4.8 +from compiler.consts import CO_VARARGS, CO_VARKEYWORDS
4.9 +
4.10 +def flatten(seq):
4.11 + l = []
4.12 + for elt in seq:
4.13 + if isinstance(elt, (tuple, list)):
4.14 + for elt2 in flatten(elt):
4.15 + l.append(elt2)
4.16 + else:
4.17 + l.append(elt)
4.18 + return l
4.19 +
4.20 +def flatten_nodes(seq):
4.21 + return [n for n in flatten(seq) if isinstance(n, Node)]
4.22 +
4.23 +def flatten_statement(seq):
4.24 + l = []
4.25 + for elt in seq:
4.26 + if isinstance(elt, Stmt):
4.27 + l += flatten_statement(elt)
4.28 + else:
4.29 + l.append(elt)
4.30 + return l
4.31 +
4.32 +def flatten_assignment(node):
4.33 + l = []
4.34 + if isinstance(node, (AssList, AssTuple)):
4.35 + for n in node.nodes:
4.36 + l += flatten_assignment(n)
4.37 + else:
4.38 + l.append(node)
4.39 + return l
4.40 +
4.41 +def is_deletion(node):
4.42 + return isinstance(node, (AssAttr, AssName)) and node.flags == "OP_DELETE"
4.43 +
4.44 +def docstring(s):
4.45 + if s.find("\n") != -1:
4.46 + if s.find("'''") != -1:
4.47 + return '"""%s"""' % s.replace('"""', '\\"\\"\\"')
4.48 + else:
4.49 + return "'''%s'''" % s.replace("'''", "\\'\\'\\'")
4.50 + else:
4.51 + return repr(s)
4.52 +
4.53 +def indent(s):
4.54 + return s.replace("\n", "\n\t")
4.55 +
4.56 +def get_defaults(node):
4.57 +
4.58 + "Return a list of (argument name, default) tuples for the 'node'."
4.59 +
4.60 + star = (node.flags & 4 != 0) and 1 or 0
4.61 + dstar = (node.flags & 8 != 0) and 1 or 0
4.62 + argnames = node.argnames[:]
4.63 +
4.64 + # Add stars to star and dstar parameters.
4.65 +
4.66 + if star:
4.67 + argnames[-dstar-star] = "*%s" % argnames[-dstar-star]
4.68 + if dstar:
4.69 + argnames[-dstar] = "**%s" % argnames[-dstar]
4.70 +
4.71 + # Map defaults to parameters.
4.72 +
4.73 + defaults = [None] * (len(node.argnames) - star - dstar - len(node.defaults)) + list(node.defaults) + [None] * (star + dstar)
4.74 + return zip(argnames, defaults)
4.75 +
4.76 +def decode_function(node):
4.77 + return [(default and "%s=%s" % (argname, default) or argname) for (argname, default) in get_defaults(node)]
4.78 +
4.79 +nodes = {}
4.80 +
4.81 +class OperatorUser:
4.82 +
4.83 + "Operator-related node."
4.84 +
4.85 + pass
4.86 +
4.87 +class Operator(OperatorUser):
4.88 +
4.89 + "Operator node."
4.90 +
4.91 + pass
4.92 +
4.93 +class Node:
4.94 +
4.95 + "Abstract base class for ast nodes."
4.96 +
4.97 + def getChildren(self):
4.98 + pass # implemented by subclasses
4.99 +
4.100 + def __iter__(self):
4.101 + for n in self.getChildren():
4.102 + yield n
4.103 +
4.104 + def getChildNodes(self):
4.105 + pass # implemented by subclasses
4.106 +
4.107 +class EmptyNode(Node):
4.108 + pass
4.109 +
4.110 +class Expression(Node):
4.111 + # Expression is an artificial node class to support "eval"
4.112 + nodes["expression"] = "Expression"
4.113 + def __init__(self, node):
4.114 + self.node = node
4.115 +
4.116 + def getChildren(self):
4.117 + return self.node,
4.118 +
4.119 + def getChildNodes(self):
4.120 + return self.node,
4.121 +
4.122 + def __repr__(self):
4.123 + return "Expression(%r)" % (self.node,)
4.124 +
4.125 + def __str__(self):
4.126 + return str(self.node)
4.127 +
4.128 +class Add(Node, Operator):
4.129 + def __init__(self, leftright, lineno=None):
4.130 + self.left = leftright[0]
4.131 + self.right = leftright[1]
4.132 + self.lineno = lineno
4.133 +
4.134 + def getChildren(self):
4.135 + return self.left, self.right
4.136 +
4.137 + def getChildNodes(self):
4.138 + return self.left, self.right
4.139 +
4.140 + def __repr__(self):
4.141 + return "Add((%r, %r))" % (self.left, self.right)
4.142 +
4.143 + def __str__(self):
4.144 + return "(%s + %s)" % (self.left, self.right)
4.145 +
4.146 +class And(Node):
4.147 + def __init__(self, nodes, lineno=None):
4.148 + self.nodes = nodes
4.149 + self.lineno = lineno
4.150 +
4.151 + def getChildren(self):
4.152 + return tuple(flatten(self.nodes))
4.153 +
4.154 + def getChildNodes(self):
4.155 + nodelist = []
4.156 + nodelist.extend(flatten_nodes(self.nodes))
4.157 + return tuple(nodelist)
4.158 +
4.159 + def __repr__(self):
4.160 + return "And(%r)" % (self.nodes,)
4.161 +
4.162 + def __str__(self):
4.163 + return "(%s)" % " and ".join(map(str, self.nodes))
4.164 +
4.165 +class AssAttr(Node):
4.166 + def __init__(self, expr, attrname, flags, lineno=None):
4.167 + self.expr = expr
4.168 + self.attrname = attrname
4.169 + self.flags = flags
4.170 + self.lineno = lineno
4.171 +
4.172 + def getChildren(self):
4.173 + return self.expr, self.attrname, self.flags
4.174 +
4.175 + def getChildNodes(self):
4.176 + return self.expr,
4.177 +
4.178 + def __repr__(self):
4.179 + return "AssAttr(%r, %r, %r)" % (self.expr, self.attrname, self.flags)
4.180 +
4.181 + def __str__(self):
4.182 + return "%s%s.%s" % (self.flags == "OP_DELETE" and "del " or "", self.expr, self.attrname)
4.183 +
4.184 +class AssList(Node):
4.185 + def __init__(self, nodes, lineno=None):
4.186 + self.nodes = nodes
4.187 + self.lineno = lineno
4.188 +
4.189 + def getChildren(self):
4.190 + return tuple(flatten(self.nodes))
4.191 +
4.192 + def getChildNodes(self):
4.193 + nodelist = []
4.194 + nodelist.extend(flatten_nodes(self.nodes))
4.195 + return tuple(nodelist)
4.196 +
4.197 + def __repr__(self):
4.198 + return "AssList(%r)" % (self.nodes,)
4.199 +
4.200 + def __str__(self):
4.201 + nodes = flatten_assignment(self)
4.202 + if nodes and is_deletion(nodes[0]):
4.203 + return "; ".join(map(str, self.nodes))
4.204 + else:
4.205 + return "[%s]" % ", ".join(map(str, self.nodes))
4.206 +
4.207 +class AssName(Node):
4.208 + def __init__(self, name, flags, lineno=None):
4.209 + self.name = name
4.210 + self.flags = flags
4.211 + self.lineno = lineno
4.212 +
4.213 + def getChildren(self):
4.214 + return self.name, self.flags
4.215 +
4.216 + def getChildNodes(self):
4.217 + return ()
4.218 +
4.219 + def __repr__(self):
4.220 + return "AssName(%r, %r)" % (self.name, self.flags)
4.221 +
4.222 + def __str__(self):
4.223 + return "%s%s" % (self.flags == "OP_DELETE" and "del " or "", self.name)
4.224 +
4.225 +class AssTuple(Node):
4.226 + def __init__(self, nodes, lineno=None):
4.227 + self.nodes = nodes
4.228 + self.lineno = lineno
4.229 +
4.230 + def getChildren(self):
4.231 + return tuple(flatten(self.nodes))
4.232 +
4.233 + def getChildNodes(self):
4.234 + nodelist = []
4.235 + nodelist.extend(flatten_nodes(self.nodes))
4.236 + return tuple(nodelist)
4.237 +
4.238 + def __repr__(self):
4.239 + return "AssTuple(%r)" % (self.nodes,)
4.240 +
4.241 + def __str__(self):
4.242 + nodes = flatten_assignment(self)
4.243 + if nodes and is_deletion(nodes[0]):
4.244 + return "; ".join(map(str, self.nodes))
4.245 + else:
4.246 + return "(%s)" % ", ".join(map(str, self.nodes))
4.247 +
4.248 +class Assert(Node):
4.249 + def __init__(self, test, fail, lineno=None):
4.250 + self.test = test
4.251 + self.fail = fail
4.252 + self.lineno = lineno
4.253 +
4.254 + def getChildren(self):
4.255 + children = []
4.256 + children.append(self.test)
4.257 + children.append(self.fail)
4.258 + return tuple(children)
4.259 +
4.260 + def getChildNodes(self):
4.261 + nodelist = []
4.262 + nodelist.append(self.test)
4.263 + if self.fail is not None:
4.264 + nodelist.append(self.fail)
4.265 + return tuple(nodelist)
4.266 +
4.267 + def __repr__(self):
4.268 + return "Assert(%r, %r)" % (self.test, self.fail)
4.269 +
4.270 + def __str__(self):
4.271 + return "assert %s%s" % (self.test, self.fail and ", %s" % self.fail or "")
4.272 +
4.273 +class Assign(Node):
4.274 + def __init__(self, nodes, expr, lineno=None):
4.275 + self.nodes = nodes
4.276 + self.expr = expr
4.277 + self.lineno = lineno
4.278 +
4.279 + def getChildren(self):
4.280 + children = []
4.281 + children.extend(flatten(self.nodes))
4.282 + children.append(self.expr)
4.283 + return tuple(children)
4.284 +
4.285 + def getChildNodes(self):
4.286 + nodelist = []
4.287 + nodelist.extend(flatten_nodes(self.nodes))
4.288 + nodelist.append(self.expr)
4.289 + return tuple(nodelist)
4.290 +
4.291 + def __repr__(self):
4.292 + return "Assign(%r, %r)" % (self.nodes, self.expr)
4.293 +
4.294 + def __str__(self):
4.295 + return "%s = %s" % (", ".join(map(str, self.nodes)), self.expr)
4.296 +
4.297 +class AugAssign(Node, OperatorUser):
4.298 + def __init__(self, node, op, expr, lineno=None):
4.299 + self.node = node
4.300 + self.op = op
4.301 + self.expr = expr
4.302 + self.lineno = lineno
4.303 +
4.304 + def getChildren(self):
4.305 + return self.node, self.op, self.expr
4.306 +
4.307 + def getChildNodes(self):
4.308 + return self.node, self.expr
4.309 +
4.310 + def __repr__(self):
4.311 + return "AugAssign(%r, %r, %r)" % (self.node, self.op, self.expr)
4.312 +
4.313 + def __str__(self):
4.314 + return "%s %s %s" % (self.node, self.op, self.expr)
4.315 +
4.316 +class Backquote(Node):
4.317 + def __init__(self, expr, lineno=None):
4.318 + self.expr = expr
4.319 + self.lineno = lineno
4.320 +
4.321 + def getChildren(self):
4.322 + return self.expr,
4.323 +
4.324 + def getChildNodes(self):
4.325 + return self.expr,
4.326 +
4.327 + def __repr__(self):
4.328 + return "Backquote(%r)" % (self.expr,)
4.329 +
4.330 + def __str__(self):
4.331 + return "`%s`" % self.expr
4.332 +
4.333 +class Bitand(Node, Operator):
4.334 + def __init__(self, nodes, lineno=None):
4.335 + self.nodes = nodes
4.336 + self.lineno = lineno
4.337 +
4.338 + def getChildren(self):
4.339 + return tuple(flatten(self.nodes))
4.340 +
4.341 + def getChildNodes(self):
4.342 + nodelist = []
4.343 + nodelist.extend(flatten_nodes(self.nodes))
4.344 + return tuple(nodelist)
4.345 +
4.346 + def __repr__(self):
4.347 + return "Bitand(%r)" % (self.nodes,)
4.348 +
4.349 + def __str__(self):
4.350 + return "(%s)" % " & ".join(map(str, self.nodes))
4.351 +
4.352 +class Bitor(Node, Operator):
4.353 + def __init__(self, nodes, lineno=None):
4.354 + self.nodes = nodes
4.355 + self.lineno = lineno
4.356 +
4.357 + def getChildren(self):
4.358 + return tuple(flatten(self.nodes))
4.359 +
4.360 + def getChildNodes(self):
4.361 + nodelist = []
4.362 + nodelist.extend(flatten_nodes(self.nodes))
4.363 + return tuple(nodelist)
4.364 +
4.365 + def __repr__(self):
4.366 + return "Bitor(%r)" % (self.nodes,)
4.367 +
4.368 + def __str__(self):
4.369 + return "(%s)" % " | ".join(map(str, self.nodes))
4.370 +
4.371 +class Bitxor(Node, Operator):
4.372 + def __init__(self, nodes, lineno=None):
4.373 + self.nodes = nodes
4.374 + self.lineno = lineno
4.375 +
4.376 + def getChildren(self):
4.377 + return tuple(flatten(self.nodes))
4.378 +
4.379 + def getChildNodes(self):
4.380 + nodelist = []
4.381 + nodelist.extend(flatten_nodes(self.nodes))
4.382 + return tuple(nodelist)
4.383 +
4.384 + def __repr__(self):
4.385 + return "Bitxor(%r)" % (self.nodes,)
4.386 +
4.387 + def __str__(self):
4.388 + return "(%s)" % " ^ ".join(map(str, self.nodes))
4.389 +
4.390 +class Break(Node):
4.391 + def __init__(self, lineno=None):
4.392 + self.lineno = lineno
4.393 +
4.394 + def getChildren(self):
4.395 + return ()
4.396 +
4.397 + def getChildNodes(self):
4.398 + return ()
4.399 +
4.400 + def __repr__(self):
4.401 + return "Break()"
4.402 +
4.403 + def __str__(self):
4.404 + return "break"
4.405 +
4.406 +class CallFunc(Node):
4.407 + def __init__(self, node, args, star_args = None, dstar_args = None, lineno=None):
4.408 + self.node = node
4.409 + self.args = args
4.410 + self.star_args = star_args
4.411 + self.dstar_args = dstar_args
4.412 + self.lineno = lineno
4.413 +
4.414 + def getChildren(self):
4.415 + children = []
4.416 + children.append(self.node)
4.417 + children.extend(flatten(self.args))
4.418 + children.append(self.star_args)
4.419 + children.append(self.dstar_args)
4.420 + return tuple(children)
4.421 +
4.422 + def getChildNodes(self):
4.423 + nodelist = []
4.424 + nodelist.append(self.node)
4.425 + nodelist.extend(flatten_nodes(self.args))
4.426 + if self.star_args is not None:
4.427 + nodelist.append(self.star_args)
4.428 + if self.dstar_args is not None:
4.429 + nodelist.append(self.dstar_args)
4.430 + return tuple(nodelist)
4.431 +
4.432 + def __repr__(self):
4.433 + args = []
4.434 + if self.dstar_args:
4.435 + args.insert(0, repr(self.dstar_args))
4.436 + if args or self.star_args:
4.437 + args.insert(0, repr(self.star_args))
4.438 + return "CallFunc(%r, %r%s)" % (self.node, self.args, args and (", %s" % ", ".join(args)) or "")
4.439 +
4.440 + def __str__(self):
4.441 + star_args = self.star_args and ["*%s" % self.star_args] or []
4.442 + dstar_args = self.dstar_args and ["**%s" % self.dstar_args] or []
4.443 + return "%s(%s)" % (self.node, ", ".join(map(str, self.args + star_args + dstar_args)))
4.444 +
4.445 +class Class(Node):
4.446 + def __init__(self, name, bases, doc, code, decorators = None, lineno=None):
4.447 + self.name = name
4.448 + self.bases = bases
4.449 + self.doc = doc
4.450 + self.code = code
4.451 + self.decorators = decorators
4.452 + self.lineno = lineno
4.453 +
4.454 + def getChildren(self):
4.455 + children = []
4.456 + children.append(self.name)
4.457 + children.extend(flatten(self.bases))
4.458 + children.append(self.doc)
4.459 + children.append(self.code)
4.460 + children.append(self.decorators)
4.461 + return tuple(children)
4.462 +
4.463 + def getChildNodes(self):
4.464 + nodelist = []
4.465 + nodelist.extend(flatten_nodes(self.bases))
4.466 + nodelist.append(self.code)
4.467 + if self.decorators is not None:
4.468 + nodelist.append(self.decorators)
4.469 + return tuple(nodelist)
4.470 +
4.471 + def __repr__(self):
4.472 + return "Class(%r, %r, %r, %r, %r)" % (self.name, self.bases, self.doc, self.code, self.decorators)
4.473 +
4.474 + def __str__(self):
4.475 + return "%sclass %s%s:%s%s\n" % (
4.476 + self.decorators and "%s\n" % "\n".join([("@%s" % decorator) for decorator in self.decorators]) or "",
4.477 + self.name,
4.478 + self.bases and "(%s)" % ", ".join(map(str, self.bases)) or "",
4.479 + self.doc and "\n\t" + docstring(self.doc) or "",
4.480 + indent("\n%s" % self.code)
4.481 + )
4.482 +
4.483 +class Compare(Node, OperatorUser):
4.484 + def __init__(self, expr, ops, lineno=None):
4.485 + self.expr = expr
4.486 + self.ops = ops
4.487 + self.lineno = lineno
4.488 +
4.489 + def getChildren(self):
4.490 + children = []
4.491 + children.append(self.expr)
4.492 + children.extend(flatten(self.ops))
4.493 + return tuple(children)
4.494 +
4.495 + def getChildNodes(self):
4.496 + nodelist = []
4.497 + nodelist.append(self.expr)
4.498 + nodelist.extend(flatten_nodes(self.ops))
4.499 + return tuple(nodelist)
4.500 +
4.501 + def __repr__(self):
4.502 + return "Compare(%r, %r)" % (self.expr, self.ops)
4.503 +
4.504 + def __str__(self):
4.505 + return "%s %s" % (self.expr, " ".join([("%s %s" % op) for op in self.ops]))
4.506 +
4.507 +class Const(Node):
4.508 + def __init__(self, value, lineno=None):
4.509 + self.value = value
4.510 + self.lineno = lineno
4.511 +
4.512 + def getChildren(self):
4.513 + return self.value,
4.514 +
4.515 + def getChildNodes(self):
4.516 + return ()
4.517 +
4.518 + def __repr__(self):
4.519 + return "Const(%r)" % (self.value,)
4.520 +
4.521 + def __str__(self):
4.522 + return repr(self.value)
4.523 +
4.524 +class Continue(Node):
4.525 + def __init__(self, lineno=None):
4.526 + self.lineno = lineno
4.527 +
4.528 + def getChildren(self):
4.529 + return ()
4.530 +
4.531 + def getChildNodes(self):
4.532 + return ()
4.533 +
4.534 + def __repr__(self):
4.535 + return "Continue()"
4.536 +
4.537 + def __str__(self):
4.538 + return "continue"
4.539 +
4.540 +class Decorators(Node):
4.541 + def __init__(self, nodes, lineno=None):
4.542 + self.nodes = nodes
4.543 + self.lineno = lineno
4.544 +
4.545 + def getChildren(self):
4.546 + return tuple(flatten(self.nodes))
4.547 +
4.548 + def getChildNodes(self):
4.549 + nodelist = []
4.550 + nodelist.extend(flatten_nodes(self.nodes))
4.551 + return tuple(nodelist)
4.552 +
4.553 + def __repr__(self):
4.554 + return "Decorators(%r)" % (self.nodes,)
4.555 +
4.556 + def __str__(self):
4.557 + return "\n".join([("@%s" % node) for node in self.nodes])
4.558 +
4.559 +class Dict(Node):
4.560 + def __init__(self, items, lineno=None):
4.561 + self.items = items
4.562 + self.lineno = lineno
4.563 +
4.564 + def getChildren(self):
4.565 + return tuple(flatten(self.items))
4.566 +
4.567 + def getChildNodes(self):
4.568 + nodelist = []
4.569 + nodelist.extend(flatten_nodes(self.items))
4.570 + return tuple(nodelist)
4.571 +
4.572 + def __repr__(self):
4.573 + return "Dict(%r)" % (self.items,)
4.574 +
4.575 + def __str__(self):
4.576 + return "{%s}" % ", ".join([("%s : %s" % (key, value)) for (key, value) in self.items])
4.577 +
4.578 +class Discard(Node):
4.579 + def __init__(self, expr, lineno=None):
4.580 + self.expr = expr
4.581 + self.lineno = lineno
4.582 +
4.583 + def getChildren(self):
4.584 + return self.expr,
4.585 +
4.586 + def getChildNodes(self):
4.587 + return self.expr,
4.588 +
4.589 + def __repr__(self):
4.590 + return "Discard(%r)" % (self.expr,)
4.591 +
4.592 + def __str__(self):
4.593 + return str(self.expr)
4.594 +
4.595 +class Div(Node, Operator):
4.596 + def __init__(self, leftright, lineno=None):
4.597 + self.left = leftright[0]
4.598 + self.right = leftright[1]
4.599 + self.lineno = lineno
4.600 +
4.601 + def getChildren(self):
4.602 + return self.left, self.right
4.603 +
4.604 + def getChildNodes(self):
4.605 + return self.left, self.right
4.606 +
4.607 + def __repr__(self):
4.608 + return "Div((%r, %r))" % (self.left, self.right)
4.609 +
4.610 + def __str__(self):
4.611 + return "(%s / %s)" % (self.left, self.right)
4.612 +
4.613 +class Ellipsis(Node):
4.614 + def __init__(self, lineno=None):
4.615 + self.lineno = lineno
4.616 +
4.617 + def getChildren(self):
4.618 + return ()
4.619 +
4.620 + def getChildNodes(self):
4.621 + return ()
4.622 +
4.623 + def __repr__(self):
4.624 + return "Ellipsis()"
4.625 +
4.626 + def __str__(self):
4.627 + return "..."
4.628 +
4.629 +class Exec(Node):
4.630 + def __init__(self, expr, locals, globals, lineno=None):
4.631 + self.expr = expr
4.632 + self.locals = locals
4.633 + self.globals = globals
4.634 + self.lineno = lineno
4.635 +
4.636 + def getChildren(self):
4.637 + children = []
4.638 + children.append(self.expr)
4.639 + children.append(self.locals)
4.640 + children.append(self.globals)
4.641 + return tuple(children)
4.642 +
4.643 + def getChildNodes(self):
4.644 + nodelist = []
4.645 + nodelist.append(self.expr)
4.646 + if self.locals is not None:
4.647 + nodelist.append(self.locals)
4.648 + if self.globals is not None:
4.649 + nodelist.append(self.globals)
4.650 + return tuple(nodelist)
4.651 +
4.652 + def __repr__(self):
4.653 + return "Exec(%r, %r, %r)" % (self.expr, self.locals, self.globals)
4.654 +
4.655 + def __str__(self):
4.656 + return "exec %s%s%s" % (self.expr, self.locals and "in %s" % self.locals or "",
4.657 + self.globals and ", %s" % self.globals or "")
4.658 +
4.659 +class FloorDiv(Node, Operator):
4.660 + def __init__(self, leftright, lineno=None):
4.661 + self.left = leftright[0]
4.662 + self.right = leftright[1]
4.663 + self.lineno = lineno
4.664 +
4.665 + def getChildren(self):
4.666 + return self.left, self.right
4.667 +
4.668 + def getChildNodes(self):
4.669 + return self.left, self.right
4.670 +
4.671 + def __repr__(self):
4.672 + return "FloorDiv((%r, %r))" % (self.left, self.right)
4.673 +
4.674 + def __str__(self):
4.675 + return "(%s // %s)" % (self.left, self.right)
4.676 +
4.677 +class For(Node):
4.678 + def __init__(self, assign, list, body, else_, lineno=None):
4.679 + self.assign = assign
4.680 + self.list = list
4.681 + self.body = body
4.682 + self.else_ = else_
4.683 + self.lineno = lineno
4.684 +
4.685 + def getChildren(self):
4.686 + children = []
4.687 + children.append(self.assign)
4.688 + children.append(self.list)
4.689 + children.append(self.body)
4.690 + children.append(self.else_)
4.691 + return tuple(children)
4.692 +
4.693 + def getChildNodes(self):
4.694 + nodelist = []
4.695 + nodelist.append(self.assign)
4.696 + nodelist.append(self.list)
4.697 + nodelist.append(self.body)
4.698 + if self.else_ is not None:
4.699 + nodelist.append(self.else_)
4.700 + return tuple(nodelist)
4.701 +
4.702 + def __repr__(self):
4.703 + return "For(%r, %r, %r, %r)" % (self.assign, self.list, self.body, self.else_)
4.704 +
4.705 + def __str__(self):
4.706 + return "for %s in %s:%s%s" % (
4.707 + self.assign, self.list,
4.708 + indent("\n%s" % self.body),
4.709 + self.else_ and "\nelse:%s" % indent("\n%s" % self.else_) or ""
4.710 + )
4.711 +
4.712 +class From(Node):
4.713 + def __init__(self, modname, names, level, lineno=None):
4.714 + self.modname = modname
4.715 + self.names = names
4.716 + self.level = level
4.717 + self.lineno = lineno
4.718 +
4.719 + def getChildren(self):
4.720 + return self.modname, self.names, self.level
4.721 +
4.722 + def getChildNodes(self):
4.723 + return ()
4.724 +
4.725 + def __repr__(self):
4.726 + return "From(%r, %r, %r)" % (self.modname, self.names, self.level)
4.727 +
4.728 + def __str__(self):
4.729 + return "from %s import %s" % (self.modname,
4.730 + ", ".join([(alias and "%s as %s" % (name, alias) or name) for (name, alias) in self.names]))
4.731 +
4.732 +class Function(Node):
4.733 + def __init__(self, decorators, name, argnames, defaults, flags, doc, code, lineno=None):
4.734 + self.decorators = decorators
4.735 + self.name = name
4.736 + self.argnames = argnames
4.737 + self.defaults = defaults
4.738 + self.flags = flags
4.739 + self.doc = doc
4.740 + self.code = code
4.741 + self.lineno = lineno
4.742 + self.varargs = self.kwargs = None
4.743 + if flags & CO_VARARGS:
4.744 + self.varargs = 1
4.745 + if flags & CO_VARKEYWORDS:
4.746 + self.kwargs = 1
4.747 +
4.748 + def getChildren(self):
4.749 + children = []
4.750 + children.append(self.decorators)
4.751 + children.append(self.name)
4.752 + children.append(self.argnames)
4.753 + children.extend(flatten(self.defaults))
4.754 + children.append(self.flags)
4.755 + children.append(self.doc)
4.756 + children.append(self.code)
4.757 + return tuple(children)
4.758 +
4.759 + def getChildNodes(self):
4.760 + nodelist = []
4.761 + if self.decorators is not None:
4.762 + nodelist.append(self.decorators)
4.763 + nodelist.extend(flatten_nodes(self.defaults))
4.764 + nodelist.append(self.code)
4.765 + return tuple(nodelist)
4.766 +
4.767 + def __repr__(self):
4.768 + return "Function(%r, %r, %r, %r, %r, %r, %r)" % (self.decorators, self.name, self.argnames, self.defaults, self.flags, self.doc, self.code)
4.769 +
4.770 + def __str__(self):
4.771 + parameters = decode_function(self)
4.772 +
4.773 + return "%sdef %s(%s):%s%s\n" % (
4.774 + self.decorators and "%s\n" % "\n".join([("@%s" % decorator) for decorator in self.decorators]) or "",
4.775 + self.name,
4.776 + ", ".join(parameters),
4.777 + self.doc and "\n\n\t%s\n" % docstring(self.doc) or "",
4.778 + indent("\n%s" % self.code)
4.779 + )
4.780 +
4.781 +class GenExpr(Node):
4.782 + def __init__(self, code, lineno=None):
4.783 + self.code = code
4.784 + self.lineno = lineno
4.785 + self.argnames = ['.0']
4.786 + self.varargs = self.kwargs = None
4.787 +
4.788 + def getChildren(self):
4.789 + return self.code,
4.790 +
4.791 + def getChildNodes(self):
4.792 + return self.code,
4.793 +
4.794 + def __repr__(self):
4.795 + return "GenExpr(%r)" % (self.code,)
4.796 +
4.797 + def __str__(self):
4.798 + return str(self.code)
4.799 +
4.800 +class GenExprFor(Node):
4.801 + def __init__(self, assign, iter, ifs, lineno=None):
4.802 + self.assign = assign
4.803 + self.iter = iter
4.804 + self.ifs = ifs
4.805 + self.lineno = lineno
4.806 + self.is_outmost = False
4.807 +
4.808 + def getChildren(self):
4.809 + children = []
4.810 + children.append(self.assign)
4.811 + children.append(self.iter)
4.812 + children.extend(flatten(self.ifs))
4.813 + return tuple(children)
4.814 +
4.815 + def getChildNodes(self):
4.816 + nodelist = []
4.817 + nodelist.append(self.assign)
4.818 + nodelist.append(self.iter)
4.819 + nodelist.extend(flatten_nodes(self.ifs))
4.820 + return tuple(nodelist)
4.821 +
4.822 + def __repr__(self):
4.823 + return "GenExprFor(%r, %r, %r)" % (self.assign, self.iter, self.ifs)
4.824 +
4.825 + def __str__(self):
4.826 + return "for %s in %s%s" % (
4.827 + self.assign, self.iter,
4.828 + self.ifs and " ".join(map(str, self.ifs)) or ""
4.829 + )
4.830 +
4.831 +class GenExprIf(Node):
4.832 + def __init__(self, test, lineno=None):
4.833 + self.test = test
4.834 + self.lineno = lineno
4.835 +
4.836 + def getChildren(self):
4.837 + return self.test,
4.838 +
4.839 + def getChildNodes(self):
4.840 + return self.test,
4.841 +
4.842 + def __repr__(self):
4.843 + return "GenExprIf(%r)" % (self.test,)
4.844 +
4.845 + def __str__(self):
4.846 + return "if %s" % self.test
4.847 +
4.848 +class GenExprInner(Node):
4.849 + def __init__(self, expr, quals, lineno=None):
4.850 + self.expr = expr
4.851 + self.quals = quals
4.852 + self.lineno = lineno
4.853 +
4.854 + def getChildren(self):
4.855 + children = []
4.856 + children.append(self.expr)
4.857 + children.extend(flatten(self.quals))
4.858 + return tuple(children)
4.859 +
4.860 + def getChildNodes(self):
4.861 + nodelist = []
4.862 + nodelist.append(self.expr)
4.863 + nodelist.extend(flatten_nodes(self.quals))
4.864 + return tuple(nodelist)
4.865 +
4.866 + def __repr__(self):
4.867 + return "GenExprInner(%r, %r)" % (self.expr, self.quals)
4.868 +
4.869 + def __str__(self):
4.870 + return "%s %s" % (self.expr, " ".join(map(str, self.quals)))
4.871 +
4.872 +class Getattr(Node):
4.873 + def __init__(self, expr, attrname, lineno=None):
4.874 + self.expr = expr
4.875 + self.attrname = attrname
4.876 + self.lineno = lineno
4.877 +
4.878 + def getChildren(self):
4.879 + return self.expr, self.attrname
4.880 +
4.881 + def getChildNodes(self):
4.882 + return self.expr,
4.883 +
4.884 + def __repr__(self):
4.885 + return "Getattr(%r, %r)" % (self.expr, self.attrname)
4.886 +
4.887 + def __str__(self):
4.888 + return "%s.%s" % (self.expr, self.attrname)
4.889 +
4.890 +class Global(Node):
4.891 + def __init__(self, names, lineno=None):
4.892 + self.names = names
4.893 + self.lineno = lineno
4.894 +
4.895 + def getChildren(self):
4.896 + return self.names,
4.897 +
4.898 + def getChildNodes(self):
4.899 + return ()
4.900 +
4.901 + def __repr__(self):
4.902 + return "Global(%r)" % (self.names,)
4.903 +
4.904 + def __str__(self):
4.905 + return "global %s" % ", ".join(map(str, self.names))
4.906 +
4.907 +class If(Node):
4.908 + def __init__(self, tests, else_, lineno=None):
4.909 + self.tests = tests
4.910 + self.else_ = else_
4.911 + self.lineno = lineno
4.912 +
4.913 + def getChildren(self):
4.914 + children = []
4.915 + children.extend(flatten(self.tests))
4.916 + children.append(self.else_)
4.917 + return tuple(children)
4.918 +
4.919 + def getChildNodes(self):
4.920 + nodelist = []
4.921 + nodelist.extend(flatten_nodes(self.tests))
4.922 + if self.else_ is not None:
4.923 + nodelist.append(self.else_)
4.924 + return tuple(nodelist)
4.925 +
4.926 + def __repr__(self):
4.927 + return "If(%r, %r)" % (self.tests, self.else_)
4.928 +
4.929 + def __str__(self):
4.930 + tests = [("%sif %s:%s" % (i > 0 and "el" or "", test, indent("\n%s" % body))) for (i, (test, body)) in enumerate(self.tests)]
4.931 + return "%s%s" % (
4.932 + "\n".join(tests),
4.933 + self.else_ and "\nelse:%s" % indent("\n%s" % self.else_) or ""
4.934 + )
4.935 +
4.936 +class IfExp(Node):
4.937 + def __init__(self, test, then, else_, lineno=None):
4.938 + self.test = test
4.939 + self.then = then
4.940 + self.else_ = else_
4.941 + self.lineno = lineno
4.942 +
4.943 + def getChildren(self):
4.944 + return self.test, self.then, self.else_
4.945 +
4.946 + def getChildNodes(self):
4.947 + return self.test, self.then, self.else_
4.948 +
4.949 + def __repr__(self):
4.950 + return "IfExp(%r, %r, %r)" % (self.test, self.then, self.else_)
4.951 +
4.952 + def __str__(self):
4.953 + return "%s if %s else %s" % (self.then, self.test, self.else_)
4.954 +
4.955 +class Import(Node):
4.956 + def __init__(self, names, lineno=None):
4.957 + self.names = names
4.958 + self.lineno = lineno
4.959 +
4.960 + def getChildren(self):
4.961 + return self.names,
4.962 +
4.963 + def getChildNodes(self):
4.964 + return ()
4.965 +
4.966 + def __repr__(self):
4.967 + return "Import(%r)" % (self.names,)
4.968 +
4.969 + def __str__(self):
4.970 + return "import %s" % (
4.971 + ", ".join([(alias and "%s as %s" % (name, alias) or name) for (name, alias) in self.names]))
4.972 +
4.973 +class Invert(Node, Operator):
4.974 + def __init__(self, expr, lineno=None):
4.975 + self.expr = expr
4.976 + self.lineno = lineno
4.977 +
4.978 + def getChildren(self):
4.979 + return self.expr,
4.980 +
4.981 + def getChildNodes(self):
4.982 + return self.expr,
4.983 +
4.984 + def __repr__(self):
4.985 + return "Invert(%r)" % (self.expr,)
4.986 +
4.987 + def __str__(self):
4.988 + return "~%s" % self.expr
4.989 +
4.990 +class Keyword(Node):
4.991 + def __init__(self, name, expr, lineno=None):
4.992 + self.name = name
4.993 + self.expr = expr
4.994 + self.lineno = lineno
4.995 +
4.996 + def getChildren(self):
4.997 + return self.name, self.expr
4.998 +
4.999 + def getChildNodes(self):
4.1000 + return self.expr,
4.1001 +
4.1002 + def __repr__(self):
4.1003 + return "Keyword(%r, %r)" % (self.name, self.expr)
4.1004 +
4.1005 + def __str__(self):
4.1006 + return "%s=%s" % (self.name, self.expr)
4.1007 +
4.1008 +class Lambda(Node):
4.1009 + def __init__(self, argnames, defaults, flags, code, lineno=None):
4.1010 + self.argnames = argnames
4.1011 + self.defaults = defaults
4.1012 + self.flags = flags
4.1013 + self.code = code
4.1014 + self.lineno = lineno
4.1015 + self.varargs = self.kwargs = None
4.1016 + if flags & CO_VARARGS:
4.1017 + self.varargs = 1
4.1018 + if flags & CO_VARKEYWORDS:
4.1019 + self.kwargs = 1
4.1020 +
4.1021 + def getChildren(self):
4.1022 + children = []
4.1023 + children.append(self.argnames)
4.1024 + children.extend(flatten(self.defaults))
4.1025 + children.append(self.flags)
4.1026 + children.append(self.code)
4.1027 + return tuple(children)
4.1028 +
4.1029 + def getChildNodes(self):
4.1030 + nodelist = []
4.1031 + nodelist.extend(flatten_nodes(self.defaults))
4.1032 + nodelist.append(self.code)
4.1033 + return tuple(nodelist)
4.1034 +
4.1035 + def __repr__(self):
4.1036 + return "Lambda(%r, %r, %r, %r)" % (self.argnames, self.defaults, self.flags, self.code)
4.1037 +
4.1038 + def __str__(self):
4.1039 + parameters = decode_function(self)
4.1040 + return "lambda %s: %s" % (", ".join(parameters), self.code)
4.1041 +
4.1042 +class LeftShift(Node, Operator):
4.1043 + def __init__(self, leftright, lineno=None):
4.1044 + self.left = leftright[0]
4.1045 + self.right = leftright[1]
4.1046 + self.lineno = lineno
4.1047 +
4.1048 + def getChildren(self):
4.1049 + return self.left, self.right
4.1050 +
4.1051 + def getChildNodes(self):
4.1052 + return self.left, self.right
4.1053 +
4.1054 + def __repr__(self):
4.1055 + return "LeftShift((%r, %r))" % (self.left, self.right)
4.1056 +
4.1057 + def __str__(self):
4.1058 + return "(%s << %s)" % (self.left, self.right)
4.1059 +
4.1060 +class List(Node):
4.1061 + def __init__(self, nodes, lineno=None):
4.1062 + self.nodes = nodes
4.1063 + self.lineno = lineno
4.1064 +
4.1065 + def getChildren(self):
4.1066 + return tuple(flatten(self.nodes))
4.1067 +
4.1068 + def getChildNodes(self):
4.1069 + nodelist = []
4.1070 + nodelist.extend(flatten_nodes(self.nodes))
4.1071 + return tuple(nodelist)
4.1072 +
4.1073 + def __repr__(self):
4.1074 + return "List(%r)" % (self.nodes,)
4.1075 +
4.1076 + def __str__(self):
4.1077 + return "[%s]" % ", ".join(map(str, self.nodes))
4.1078 +
4.1079 +class ListComp(Node):
4.1080 + def __init__(self, expr, quals, lineno=None):
4.1081 + self.expr = expr
4.1082 + self.quals = quals
4.1083 + self.lineno = lineno
4.1084 +
4.1085 + def getChildren(self):
4.1086 + children = []
4.1087 + children.append(self.expr)
4.1088 + children.extend(flatten(self.quals))
4.1089 + return tuple(children)
4.1090 +
4.1091 + def getChildNodes(self):
4.1092 + nodelist = []
4.1093 + nodelist.append(self.expr)
4.1094 + nodelist.extend(flatten_nodes(self.quals))
4.1095 + return tuple(nodelist)
4.1096 +
4.1097 + def __repr__(self):
4.1098 + return "ListComp(%r, %r)" % (self.expr, self.quals)
4.1099 +
4.1100 + def __str__(self):
4.1101 + return "[%s %s]" % (self.expr, " ".join(map(str, self.quals)))
4.1102 +
4.1103 +class ListCompFor(Node):
4.1104 + def __init__(self, assign, list, ifs, lineno=None):
4.1105 + self.assign = assign
4.1106 + self.list = list
4.1107 + self.ifs = ifs
4.1108 + self.lineno = lineno
4.1109 +
4.1110 + def getChildren(self):
4.1111 + children = []
4.1112 + children.append(self.assign)
4.1113 + children.append(self.list)
4.1114 + children.extend(flatten(self.ifs))
4.1115 + return tuple(children)
4.1116 +
4.1117 + def getChildNodes(self):
4.1118 + nodelist = []
4.1119 + nodelist.append(self.assign)
4.1120 + nodelist.append(self.list)
4.1121 + nodelist.extend(flatten_nodes(self.ifs))
4.1122 + return tuple(nodelist)
4.1123 +
4.1124 + def __repr__(self):
4.1125 + return "ListCompFor(%r, %r, %r)" % (self.assign, self.list, self.ifs)
4.1126 +
4.1127 + def __str__(self):
4.1128 + return "for %s in %s%s" % (
4.1129 + self.assign, self.list,
4.1130 + self.ifs and " ".join(map(str, self.ifs)) or ""
4.1131 + )
4.1132 +
4.1133 +class ListCompIf(Node):
4.1134 + def __init__(self, test, lineno=None):
4.1135 + self.test = test
4.1136 + self.lineno = lineno
4.1137 +
4.1138 + def getChildren(self):
4.1139 + return self.test,
4.1140 +
4.1141 + def getChildNodes(self):
4.1142 + return self.test,
4.1143 +
4.1144 + def __repr__(self):
4.1145 + return "ListCompIf(%r)" % (self.test,)
4.1146 +
4.1147 + def __str__(self):
4.1148 + return " if %s" % self.test
4.1149 +
4.1150 +class SetComp(Node):
4.1151 + def __init__(self, expr, quals, lineno=None):
4.1152 + self.expr = expr
4.1153 + self.quals = quals
4.1154 + self.lineno = lineno
4.1155 +
4.1156 + def getChildren(self):
4.1157 + children = []
4.1158 + children.append(self.expr)
4.1159 + children.extend(flatten(self.quals))
4.1160 + return tuple(children)
4.1161 +
4.1162 + def getChildNodes(self):
4.1163 + nodelist = []
4.1164 + nodelist.append(self.expr)
4.1165 + nodelist.extend(flatten_nodes(self.quals))
4.1166 + return tuple(nodelist)
4.1167 +
4.1168 + def __repr__(self):
4.1169 + return "SetComp(%r, %r)" % (self.expr, self.quals)
4.1170 +
4.1171 + def __str__(self):
4.1172 + return "{%s %s}" % (self.expr, " ".join(map(str, self.quals)))
4.1173 +
4.1174 +class DictComp(Node):
4.1175 + def __init__(self, key, value, quals, lineno=None):
4.1176 + self.key = key
4.1177 + self.value = value
4.1178 + self.quals = quals
4.1179 + self.lineno = lineno
4.1180 +
4.1181 + def getChildren(self):
4.1182 + children = []
4.1183 + children.append(self.key)
4.1184 + children.append(self.value)
4.1185 + children.extend(flatten(self.quals))
4.1186 + return tuple(children)
4.1187 +
4.1188 + def getChildNodes(self):
4.1189 + nodelist = []
4.1190 + nodelist.append(self.key)
4.1191 + nodelist.append(self.value)
4.1192 + nodelist.extend(flatten_nodes(self.quals))
4.1193 + return tuple(nodelist)
4.1194 +
4.1195 + def __repr__(self):
4.1196 + return "DictComp(%r, %r, %r)" % (self.key, self.value, self.quals)
4.1197 +
4.1198 + def __str__(self):
4.1199 + return "{%s : %s %s}" % (self.key, self.value, " ".join(map(str, self.quals)))
4.1200 +
4.1201 +class Mod(Node, Operator):
4.1202 + def __init__(self, leftright, lineno=None):
4.1203 + self.left = leftright[0]
4.1204 + self.right = leftright[1]
4.1205 + self.lineno = lineno
4.1206 +
4.1207 + def getChildren(self):
4.1208 + return self.left, self.right
4.1209 +
4.1210 + def getChildNodes(self):
4.1211 + return self.left, self.right
4.1212 +
4.1213 + def __repr__(self):
4.1214 + return "Mod((%r, %r))" % (self.left, self.right)
4.1215 +
4.1216 + def __str__(self):
4.1217 + return "(%s %% %s)" % (self.left, self.right)
4.1218 +
4.1219 +class Module(Node):
4.1220 + def __init__(self, doc, node, lineno=None):
4.1221 + self.doc = doc
4.1222 + self.node = node
4.1223 + self.lineno = lineno
4.1224 +
4.1225 + def getChildren(self):
4.1226 + return self.doc, self.node
4.1227 +
4.1228 + def getChildNodes(self):
4.1229 + return self.node,
4.1230 +
4.1231 + def __repr__(self):
4.1232 + return "Module(%r, %r)" % (self.doc, self.node)
4.1233 +
4.1234 + def __str__(self):
4.1235 + return "%s%s" % (self.doc and "%s\n\n" % docstring(self.doc) or "", self.node)
4.1236 +
4.1237 +class Mul(Node, Operator):
4.1238 + def __init__(self, leftright, lineno=None):
4.1239 + self.left = leftright[0]
4.1240 + self.right = leftright[1]
4.1241 + self.lineno = lineno
4.1242 +
4.1243 + def getChildren(self):
4.1244 + return self.left, self.right
4.1245 +
4.1246 + def getChildNodes(self):
4.1247 + return self.left, self.right
4.1248 +
4.1249 + def __repr__(self):
4.1250 + return "Mul((%r, %r))" % (self.left, self.right)
4.1251 +
4.1252 + def __str__(self):
4.1253 + return "(%s * %s)" % (self.left, self.right)
4.1254 +
4.1255 +class Name(Node):
4.1256 + def __init__(self, name, lineno=None):
4.1257 + self.name = name
4.1258 + self.lineno = lineno
4.1259 +
4.1260 + def getChildren(self):
4.1261 + return self.name,
4.1262 +
4.1263 + def getChildNodes(self):
4.1264 + return ()
4.1265 +
4.1266 + def __repr__(self):
4.1267 + return "Name(%r)" % (self.name,)
4.1268 +
4.1269 + def __str__(self):
4.1270 + return str(self.name)
4.1271 +
4.1272 +class Not(Node):
4.1273 + def __init__(self, expr, lineno=None):
4.1274 + self.expr = expr
4.1275 + self.lineno = lineno
4.1276 +
4.1277 + def getChildren(self):
4.1278 + return self.expr,
4.1279 +
4.1280 + def getChildNodes(self):
4.1281 + return self.expr,
4.1282 +
4.1283 + def __repr__(self):
4.1284 + return "Not(%r)" % (self.expr,)
4.1285 +
4.1286 + def __str__(self):
4.1287 + return "not %s" % self.expr
4.1288 +
4.1289 +class Or(Node):
4.1290 + def __init__(self, nodes, lineno=None):
4.1291 + self.nodes = nodes
4.1292 + self.lineno = lineno
4.1293 +
4.1294 + def getChildren(self):
4.1295 + return tuple(flatten(self.nodes))
4.1296 +
4.1297 + def getChildNodes(self):
4.1298 + nodelist = []
4.1299 + nodelist.extend(flatten_nodes(self.nodes))
4.1300 + return tuple(nodelist)
4.1301 +
4.1302 + def __repr__(self):
4.1303 + return "Or(%r)" % (self.nodes,)
4.1304 +
4.1305 + def __str__(self):
4.1306 + return "(%s)" % " or ".join(map(str, self.nodes))
4.1307 +
4.1308 +class Pass(Node):
4.1309 + def __init__(self, lineno=None):
4.1310 + self.lineno = lineno
4.1311 +
4.1312 + def getChildren(self):
4.1313 + return ()
4.1314 +
4.1315 + def getChildNodes(self):
4.1316 + return ()
4.1317 +
4.1318 + def __repr__(self):
4.1319 + return "Pass()"
4.1320 +
4.1321 + def __str__(self):
4.1322 + return "pass"
4.1323 +
4.1324 +class Power(Node, Operator):
4.1325 + def __init__(self, leftright, lineno=None):
4.1326 + self.left = leftright[0]
4.1327 + self.right = leftright[1]
4.1328 + self.lineno = lineno
4.1329 +
4.1330 + def getChildren(self):
4.1331 + return self.left, self.right
4.1332 +
4.1333 + def getChildNodes(self):
4.1334 + return self.left, self.right
4.1335 +
4.1336 + def __repr__(self):
4.1337 + return "Power((%r, %r))" % (self.left, self.right)
4.1338 +
4.1339 + def __str__(self):
4.1340 + return "(%s ** %s)" % (self.left, self.right)
4.1341 +
4.1342 +class Print(Node):
4.1343 + def __init__(self, nodes, dest, lineno=None):
4.1344 + self.nodes = nodes
4.1345 + self.dest = dest
4.1346 + self.lineno = lineno
4.1347 +
4.1348 + def getChildren(self):
4.1349 + children = []
4.1350 + children.extend(flatten(self.nodes))
4.1351 + children.append(self.dest)
4.1352 + return tuple(children)
4.1353 +
4.1354 + def getChildNodes(self):
4.1355 + nodelist = []
4.1356 + nodelist.extend(flatten_nodes(self.nodes))
4.1357 + if self.dest is not None:
4.1358 + nodelist.append(self.dest)
4.1359 + return tuple(nodelist)
4.1360 +
4.1361 + def __repr__(self):
4.1362 + return "Print(%r, %r)" % (self.nodes, self.dest)
4.1363 +
4.1364 + def __str__(self):
4.1365 + dest = self.dest and [">>%s" % self.dest] or []
4.1366 + return "print %s," % ", ".join(map(str, dest + self.nodes))
4.1367 +
4.1368 +class Printnl(Node):
4.1369 + def __init__(self, nodes, dest, lineno=None):
4.1370 + self.nodes = nodes
4.1371 + self.dest = dest
4.1372 + self.lineno = lineno
4.1373 +
4.1374 + def getChildren(self):
4.1375 + children = []
4.1376 + children.extend(flatten(self.nodes))
4.1377 + children.append(self.dest)
4.1378 + return tuple(children)
4.1379 +
4.1380 + def getChildNodes(self):
4.1381 + nodelist = []
4.1382 + nodelist.extend(flatten_nodes(self.nodes))
4.1383 + if self.dest is not None:
4.1384 + nodelist.append(self.dest)
4.1385 + return tuple(nodelist)
4.1386 +
4.1387 + def __repr__(self):
4.1388 + return "Printnl(%r, %r)" % (self.nodes, self.dest)
4.1389 +
4.1390 + def __str__(self):
4.1391 + dest = self.dest and [">>%s" % self.dest] or []
4.1392 + return "print %s" % ", ".join(map(str, dest + self.nodes))
4.1393 +
4.1394 +class Raise(Node):
4.1395 + def __init__(self, expr1, expr2, expr3, lineno=None):
4.1396 + self.expr1 = expr1
4.1397 + self.expr2 = expr2
4.1398 + self.expr3 = expr3
4.1399 + self.lineno = lineno
4.1400 +
4.1401 + def getChildren(self):
4.1402 + children = []
4.1403 + children.append(self.expr1)
4.1404 + children.append(self.expr2)
4.1405 + children.append(self.expr3)
4.1406 + return tuple(children)
4.1407 +
4.1408 + def getChildNodes(self):
4.1409 + nodelist = []
4.1410 + if self.expr1 is not None:
4.1411 + nodelist.append(self.expr1)
4.1412 + if self.expr2 is not None:
4.1413 + nodelist.append(self.expr2)
4.1414 + if self.expr3 is not None:
4.1415 + nodelist.append(self.expr3)
4.1416 + return tuple(nodelist)
4.1417 +
4.1418 + def __repr__(self):
4.1419 + return "Raise(%r, %r, %r)" % (self.expr1, self.expr2, self.expr3)
4.1420 +
4.1421 + def __str__(self):
4.1422 + args = self.expr1 and [self.expr1] or []
4.1423 + args += self.expr2 and [self.expr2] or []
4.1424 + args += self.expr3 and [self.expr3] or []
4.1425 + return "raise %s" % ", ".join(map(str, args))
4.1426 +
4.1427 +class Return(Node):
4.1428 + def __init__(self, value, lineno=None):
4.1429 + self.value = value
4.1430 + self.lineno = lineno
4.1431 +
4.1432 + def getChildren(self):
4.1433 + return self.value,
4.1434 +
4.1435 + def getChildNodes(self):
4.1436 + return self.value,
4.1437 +
4.1438 + def __repr__(self):
4.1439 + return "Return(%r)" % (self.value,)
4.1440 +
4.1441 + def __str__(self):
4.1442 + return "return %s" % self.value
4.1443 +
4.1444 +class RightShift(Node, Operator):
4.1445 + def __init__(self, leftright, lineno=None):
4.1446 + self.left = leftright[0]
4.1447 + self.right = leftright[1]
4.1448 + self.lineno = lineno
4.1449 +
4.1450 + def getChildren(self):
4.1451 + return self.left, self.right
4.1452 +
4.1453 + def getChildNodes(self):
4.1454 + return self.left, self.right
4.1455 +
4.1456 + def __repr__(self):
4.1457 + return "RightShift((%r, %r))" % (self.left, self.right)
4.1458 +
4.1459 + def __str__(self):
4.1460 + return "(%s >> %s)" % (self.left, self.right)
4.1461 +
4.1462 +class Set(Node):
4.1463 + def __init__(self, nodes, lineno=None):
4.1464 + self.nodes = nodes
4.1465 + self.lineno = lineno
4.1466 +
4.1467 + def getChildren(self):
4.1468 + return tuple(flatten(self.nodes))
4.1469 +
4.1470 + def getChildNodes(self):
4.1471 + nodelist = []
4.1472 + nodelist.extend(flatten_nodes(self.nodes))
4.1473 + return tuple(nodelist)
4.1474 +
4.1475 + def __repr__(self):
4.1476 + return "Set(%r)" % (self.nodes,)
4.1477 +
4.1478 + def __str__(self):
4.1479 + return "{%s}" % ", ".join(map(str, self.nodes))
4.1480 +
4.1481 +class Slice(Node, OperatorUser):
4.1482 + def __init__(self, expr, flags, lower, upper, lineno=None):
4.1483 + self.expr = expr
4.1484 + self.flags = flags
4.1485 + self.lower = lower
4.1486 + self.upper = upper
4.1487 + self.lineno = lineno
4.1488 +
4.1489 + def getChildren(self):
4.1490 + children = []
4.1491 + children.append(self.expr)
4.1492 + children.append(self.flags)
4.1493 + children.append(self.lower)
4.1494 + children.append(self.upper)
4.1495 + return tuple(children)
4.1496 +
4.1497 + def getChildNodes(self):
4.1498 + nodelist = []
4.1499 + nodelist.append(self.expr)
4.1500 + if self.lower is not None:
4.1501 + nodelist.append(self.lower)
4.1502 + if self.upper is not None:
4.1503 + nodelist.append(self.upper)
4.1504 + return tuple(nodelist)
4.1505 +
4.1506 + def __repr__(self):
4.1507 + return "Slice(%r, %r, %r, %r)" % (self.expr, self.flags, self.lower, self.upper)
4.1508 +
4.1509 + def __str__(self):
4.1510 + args = [self.lower or "", self.upper or ""]
4.1511 + return "%s%s[%s]" % (self.flags == "OP_DELETE" and "del " or "", self.expr, ":".join(map(str, args)))
4.1512 +
4.1513 +class Sliceobj(Node):
4.1514 + def __init__(self, nodes, lineno=None):
4.1515 + self.nodes = nodes
4.1516 + self.lineno = lineno
4.1517 +
4.1518 + def getChildren(self):
4.1519 + return tuple(flatten(self.nodes))
4.1520 +
4.1521 + def getChildNodes(self):
4.1522 + nodelist = []
4.1523 + nodelist.extend(flatten_nodes(self.nodes))
4.1524 + return tuple(nodelist)
4.1525 +
4.1526 + def __repr__(self):
4.1527 + return "Sliceobj(%r)" % (self.nodes,)
4.1528 +
4.1529 + def __str__(self):
4.1530 + return ":".join(map(str, self.nodes))
4.1531 +
4.1532 +class Stmt(Node):
4.1533 + def __init__(self, nodes, lineno=None):
4.1534 + self.nodes = nodes
4.1535 + self.lineno = lineno
4.1536 +
4.1537 + def getChildren(self):
4.1538 + return tuple(flatten(self.nodes))
4.1539 +
4.1540 + def getChildNodes(self):
4.1541 + nodelist = []
4.1542 + nodelist.extend(flatten_nodes(self.nodes))
4.1543 + return tuple(nodelist)
4.1544 +
4.1545 + def __repr__(self):
4.1546 + return "Stmt(%r)" % (self.nodes,)
4.1547 +
4.1548 + def __str__(self):
4.1549 + return "\n".join(map(str, flatten_statement(self.nodes)))
4.1550 +
4.1551 +class Sub(Node, Operator):
4.1552 + def __init__(self, leftright, lineno=None):
4.1553 + self.left = leftright[0]
4.1554 + self.right = leftright[1]
4.1555 + self.lineno = lineno
4.1556 +
4.1557 + def getChildren(self):
4.1558 + return self.left, self.right
4.1559 +
4.1560 + def getChildNodes(self):
4.1561 + return self.left, self.right
4.1562 +
4.1563 + def __repr__(self):
4.1564 + return "Sub((%r, %r))" % (self.left, self.right)
4.1565 +
4.1566 + def __str__(self):
4.1567 + return "(%s - %s)" % (self.left, self.right)
4.1568 +
4.1569 +class Subscript(Node, OperatorUser):
4.1570 + def __init__(self, expr, flags, subs, lineno=None):
4.1571 + self.expr = expr
4.1572 + self.flags = flags
4.1573 + self.subs = subs
4.1574 + self.lineno = lineno
4.1575 +
4.1576 + def getChildren(self):
4.1577 + children = []
4.1578 + children.append(self.expr)
4.1579 + children.append(self.flags)
4.1580 + children.extend(flatten(self.subs))
4.1581 + return tuple(children)
4.1582 +
4.1583 + def getChildNodes(self):
4.1584 + nodelist = []
4.1585 + nodelist.append(self.expr)
4.1586 + nodelist.extend(flatten_nodes(self.subs))
4.1587 + return tuple(nodelist)
4.1588 +
4.1589 + def __repr__(self):
4.1590 + return "Subscript(%r, %r, %r)" % (self.expr, self.flags, self.subs)
4.1591 +
4.1592 + def __str__(self):
4.1593 + return "%s%s[%s]" % (self.flags == "OP_DELETE" and "del " or "", self.expr, ",".join(map(str, self.subs)))
4.1594 +
4.1595 +class TryExcept(Node):
4.1596 + def __init__(self, body, handlers, else_, lineno=None):
4.1597 + self.body = body
4.1598 + self.handlers = handlers
4.1599 + self.else_ = else_
4.1600 + self.lineno = lineno
4.1601 +
4.1602 + def getChildren(self):
4.1603 + children = []
4.1604 + children.append(self.body)
4.1605 + children.extend(flatten(self.handlers))
4.1606 + children.append(self.else_)
4.1607 + return tuple(children)
4.1608 +
4.1609 + def getChildNodes(self):
4.1610 + nodelist = []
4.1611 + nodelist.append(self.body)
4.1612 + nodelist.extend(flatten_nodes(self.handlers))
4.1613 + if self.else_ is not None:
4.1614 + nodelist.append(self.else_)
4.1615 + return tuple(nodelist)
4.1616 +
4.1617 + def __repr__(self):
4.1618 + return "TryExcept(%r, %r, %r)" % (self.body, self.handlers, self.else_)
4.1619 +
4.1620 + def __str__(self):
4.1621 + handlers = [
4.1622 + ("\nexcept%s%s:%s" % (spec and " %s" % spec or "", assign and ", %s" % assign or "", indent("\n%s" % statement)))
4.1623 + for (spec, assign, statement) in self.handlers
4.1624 + ]
4.1625 +
4.1626 + return "try:%s%s%s" % (
4.1627 + indent("\n%s" % self.body),
4.1628 + "".join(handlers),
4.1629 + self.else_ and "\nelse:%s" % indent("\n%s" % self.else_) or ""
4.1630 + )
4.1631 +
4.1632 +class TryFinally(Node):
4.1633 + def __init__(self, body, final, lineno=None):
4.1634 + self.body = body
4.1635 + self.final = final
4.1636 + self.lineno = lineno
4.1637 +
4.1638 + def getChildren(self):
4.1639 + return self.body, self.final
4.1640 +
4.1641 + def getChildNodes(self):
4.1642 + return self.body, self.final
4.1643 +
4.1644 + def __repr__(self):
4.1645 + return "TryFinally(%r, %r)" % (self.body, self.final)
4.1646 +
4.1647 + def __str__(self):
4.1648 + return "try:%s\nfinally:%s" % (
4.1649 + indent("\n%s" % self.body),
4.1650 + indent("\n%s" % self.final)
4.1651 + )
4.1652 +
4.1653 +class Tuple(Node):
4.1654 + def __init__(self, nodes, lineno=None):
4.1655 + self.nodes = nodes
4.1656 + self.lineno = lineno
4.1657 +
4.1658 + def getChildren(self):
4.1659 + return tuple(flatten(self.nodes))
4.1660 +
4.1661 + def getChildNodes(self):
4.1662 + nodelist = []
4.1663 + nodelist.extend(flatten_nodes(self.nodes))
4.1664 + return tuple(nodelist)
4.1665 +
4.1666 + def __repr__(self):
4.1667 + return "Tuple(%r)" % (self.nodes,)
4.1668 +
4.1669 + def __str__(self):
4.1670 + return "(%s)" % ", ".join(map(str, self.nodes))
4.1671 +
4.1672 +class UnaryAdd(Node, Operator):
4.1673 + def __init__(self, expr, lineno=None):
4.1674 + self.expr = expr
4.1675 + self.lineno = lineno
4.1676 +
4.1677 + def getChildren(self):
4.1678 + return self.expr,
4.1679 +
4.1680 + def getChildNodes(self):
4.1681 + return self.expr,
4.1682 +
4.1683 + def __repr__(self):
4.1684 + return "UnaryAdd(%r)" % (self.expr,)
4.1685 +
4.1686 + def __str__(self):
4.1687 + return "+%s" % self.expr
4.1688 +
4.1689 +class UnarySub(Node, Operator):
4.1690 + def __init__(self, expr, lineno=None):
4.1691 + self.expr = expr
4.1692 + self.lineno = lineno
4.1693 +
4.1694 + def getChildren(self):
4.1695 + return self.expr,
4.1696 +
4.1697 + def getChildNodes(self):
4.1698 + return self.expr,
4.1699 +
4.1700 + def __repr__(self):
4.1701 + return "UnarySub(%r)" % (self.expr,)
4.1702 +
4.1703 + def __str__(self):
4.1704 + return "-%s" % self.expr
4.1705 +
4.1706 +class While(Node):
4.1707 + def __init__(self, test, body, else_, lineno=None):
4.1708 + self.test = test
4.1709 + self.body = body
4.1710 + self.else_ = else_
4.1711 + self.lineno = lineno
4.1712 +
4.1713 + def getChildren(self):
4.1714 + children = []
4.1715 + children.append(self.test)
4.1716 + children.append(self.body)
4.1717 + children.append(self.else_)
4.1718 + return tuple(children)
4.1719 +
4.1720 + def getChildNodes(self):
4.1721 + nodelist = []
4.1722 + nodelist.append(self.test)
4.1723 + nodelist.append(self.body)
4.1724 + if self.else_ is not None:
4.1725 + nodelist.append(self.else_)
4.1726 + return tuple(nodelist)
4.1727 +
4.1728 + def __repr__(self):
4.1729 + return "While(%r, %r, %r)" % (self.test, self.body, self.else_)
4.1730 +
4.1731 + def __str__(self):
4.1732 + return "while %s:%s%s" % (
4.1733 + self.test,
4.1734 + indent("\n%s" % self.body),
4.1735 + self.else_ and "\nelse:%s" % indent("\n%s" % self.else_) or ""
4.1736 + )
4.1737 +
4.1738 +class With(Node):
4.1739 + def __init__(self, expr, vars, body, lineno=None):
4.1740 + self.expr = expr
4.1741 + self.vars = vars
4.1742 + self.body = body
4.1743 + self.lineno = lineno
4.1744 +
4.1745 + def getChildren(self):
4.1746 + children = []
4.1747 + children.append(self.expr)
4.1748 + children.append(self.vars)
4.1749 + children.append(self.body)
4.1750 + return tuple(children)
4.1751 +
4.1752 + def getChildNodes(self):
4.1753 + nodelist = []
4.1754 + nodelist.append(self.expr)
4.1755 + if self.vars is not None:
4.1756 + nodelist.append(self.vars)
4.1757 + nodelist.append(self.body)
4.1758 + return tuple(nodelist)
4.1759 +
4.1760 + def __repr__(self):
4.1761 + return "With(%r, %r, %r)" % (self.expr, self.vars, self.body)
4.1762 +
4.1763 + def __str__(self):
4.1764 + return "with %s%s:%s" % (
4.1765 + self.expr,
4.1766 + self.vars and " as %s" % ", ".join(map(str, self.vars)),
4.1767 + indent("\n%s" % self.body),
4.1768 + )
4.1769 +
4.1770 +class Yield(Node):
4.1771 + def __init__(self, value, lineno=None):
4.1772 + self.value = value
4.1773 + self.lineno = lineno
4.1774 +
4.1775 + def getChildren(self):
4.1776 + return self.value,
4.1777 +
4.1778 + def getChildNodes(self):
4.1779 + return self.value,
4.1780 +
4.1781 + def __repr__(self):
4.1782 + return "Yield(%r)" % (self.value,)
4.1783 +
4.1784 + def __str__(self):
4.1785 + return "yield %s" % self.value
4.1786 +
4.1787 +for name, obj in globals().items():
4.1788 + if isinstance(obj, type) and issubclass(obj, Node):
4.1789 + nodes[name.lower()] = obj
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/compiler/consts.py Tue Aug 30 16:51:10 2016 +0200
5.3 @@ -0,0 +1,23 @@
5.4 +# operation flags
5.5 +OP_ASSIGN = 'OP_ASSIGN'
5.6 +OP_DELETE = 'OP_DELETE'
5.7 +OP_APPLY = 'OP_APPLY'
5.8 +
5.9 +SC_LOCAL = 1
5.10 +SC_GLOBAL_IMPLICIT = 2
5.11 +SC_GLOBAL_EXPLICT = 3
5.12 +SC_FREE = 4
5.13 +SC_CELL = 5
5.14 +SC_UNKNOWN = 6
5.15 +
5.16 +CO_OPTIMIZED = 0x0001
5.17 +CO_NEWLOCALS = 0x0002
5.18 +CO_VARARGS = 0x0004
5.19 +CO_VARKEYWORDS = 0x0008
5.20 +CO_NESTED = 0x0010
5.21 +CO_GENERATOR = 0x0020
5.22 +CO_GENERATOR_ALLOWED = 0
5.23 +CO_FUTURE_DIVISION = 0x2000
5.24 +CO_FUTURE_ABSIMPORT = 0x4000
5.25 +CO_FUTURE_WITH_STATEMENT = 0x8000
5.26 +CO_FUTURE_PRINT_FUNCTION = 0x10000
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/compiler/transformer.py Tue Aug 30 16:51:10 2016 +0200
6.3 @@ -0,0 +1,1515 @@
6.4 +"""Parse tree transformation module.
6.5 +
6.6 +Transforms Python source code into an abstract syntax tree (AST)
6.7 +defined in the ast module.
6.8 +
6.9 +The simplest ways to invoke this module are via parse and parseFile.
6.10 +parse(buf) -> AST
6.11 +parseFile(path) -> AST
6.12 +"""
6.13 +
6.14 +# Original version written by Greg Stein (gstein@lyra.org)
6.15 +# and Bill Tutt (rassilon@lima.mudlib.org)
6.16 +# February 1997.
6.17 +#
6.18 +# Modifications and improvements for Python 2.0 by Jeremy Hylton and
6.19 +# Mark Hammond
6.20 +#
6.21 +# Some fixes to try to have correct line number on almost all nodes
6.22 +# (except Module, Discard and Stmt) added by Sylvain Thenault
6.23 +#
6.24 +# Portions of this file are:
6.25 +# Copyright (C) 1997-1998 Greg Stein. All Rights Reserved.
6.26 +#
6.27 +# This module is provided under a BSD-ish license. See
6.28 +# http://www.opensource.org/licenses/bsd-license.html
6.29 +# and replace OWNER, ORGANIZATION, and YEAR as appropriate.
6.30 +
6.31 +from compiler.ast import *
6.32 +import parser
6.33 +import symbol
6.34 +import token
6.35 +
6.36 +class WalkerError(StandardError):
6.37 + pass
6.38 +
6.39 +from compiler.consts import CO_VARARGS, CO_VARKEYWORDS
6.40 +from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY
6.41 +
6.42 +def parseFile(path):
6.43 + f = open(path, "U")
6.44 + # XXX The parser API tolerates files without a trailing newline,
6.45 + # but not strings without a trailing newline. Always add an extra
6.46 + # newline to the file contents, since we're going through the string
6.47 + # version of the API.
6.48 + src = f.read() + "\n"
6.49 + f.close()
6.50 + return parse(src)
6.51 +
6.52 +def parse(buf, mode="exec"):
6.53 + if mode == "exec" or mode == "single":
6.54 + return Transformer().parsesuite(buf)
6.55 + elif mode == "eval":
6.56 + return Transformer().parseexpr(buf)
6.57 + else:
6.58 + raise ValueError("compile() arg 3 must be"
6.59 + " 'exec' or 'eval' or 'single'")
6.60 +
6.61 +def extractLineNo(ast):
6.62 + if not isinstance(ast[1], tuple):
6.63 + # get a terminal node
6.64 + return ast[2]
6.65 + for child in ast[1:]:
6.66 + if isinstance(child, tuple):
6.67 + lineno = extractLineNo(child)
6.68 + if lineno is not None:
6.69 + return lineno
6.70 +
6.71 +def Node(*args):
6.72 + kind = args[0]
6.73 + if kind in nodes:
6.74 + try:
6.75 + return nodes[kind](*args[1:])
6.76 + except TypeError:
6.77 + print nodes[kind], len(args), args
6.78 + raise
6.79 + else:
6.80 + raise WalkerError, "Can't find appropriate Node type: %s" % str(args)
6.81 + #return apply(ast.Node, args)
6.82 +
6.83 +class Transformer:
6.84 + """Utility object for transforming Python parse trees.
6.85 +
6.86 + Exposes the following methods:
6.87 + tree = transform(ast_tree)
6.88 + tree = parsesuite(text)
6.89 + tree = parseexpr(text)
6.90 + tree = parsefile(fileob | filename)
6.91 + """
6.92 +
6.93 + def __init__(self):
6.94 + self._dispatch = {}
6.95 + for value, name in symbol.sym_name.items():
6.96 + if hasattr(self, name):
6.97 + self._dispatch[value] = getattr(self, name)
6.98 + self._dispatch[token.NEWLINE] = self.com_NEWLINE
6.99 + self._atom_dispatch = {token.LPAR: self.atom_lpar,
6.100 + token.LSQB: self.atom_lsqb,
6.101 + token.LBRACE: self.atom_lbrace,
6.102 + token.BACKQUOTE: self.atom_backquote,
6.103 + token.NUMBER: self.atom_number,
6.104 + token.STRING: self.atom_string,
6.105 + token.NAME: self.atom_name,
6.106 + }
6.107 + self.encoding = None
6.108 +
6.109 + def transform(self, tree):
6.110 + """Transform an AST into a modified parse tree."""
6.111 + if not (isinstance(tree, tuple) or isinstance(tree, list)):
6.112 + tree = parser.st2tuple(tree, line_info=1)
6.113 + return self.compile_node(tree)
6.114 +
6.115 + def parsesuite(self, text):
6.116 + """Return a modified parse tree for the given suite text."""
6.117 + return self.transform(parser.suite(text))
6.118 +
6.119 + def parseexpr(self, text):
6.120 + """Return a modified parse tree for the given expression text."""
6.121 + return self.transform(parser.expr(text))
6.122 +
6.123 + def parsefile(self, file):
6.124 + """Return a modified parse tree for the contents of the given file."""
6.125 + if type(file) == type(''):
6.126 + file = open(file)
6.127 + return self.parsesuite(file.read())
6.128 +
6.129 + # --------------------------------------------------------------
6.130 + #
6.131 + # PRIVATE METHODS
6.132 + #
6.133 +
6.134 + def compile_node(self, node):
6.135 + ### emit a line-number node?
6.136 + n = node[0]
6.137 +
6.138 + if n == symbol.encoding_decl:
6.139 + self.encoding = node[2]
6.140 + node = node[1]
6.141 + n = node[0]
6.142 +
6.143 + if n == symbol.single_input:
6.144 + return self.single_input(node[1:])
6.145 + if n == symbol.file_input:
6.146 + return self.file_input(node[1:])
6.147 + if n == symbol.eval_input:
6.148 + return self.eval_input(node[1:])
6.149 + if n == symbol.lambdef:
6.150 + return self.lambdef(node[1:])
6.151 + if n == symbol.funcdef:
6.152 + return self.funcdef(node[1:])
6.153 + if n == symbol.classdef:
6.154 + return self.classdef(node[1:])
6.155 +
6.156 + raise WalkerError, ('unexpected node type', n)
6.157 +
6.158 + def single_input(self, node):
6.159 + ### do we want to do anything about being "interactive" ?
6.160 +
6.161 + # NEWLINE | simple_stmt | compound_stmt NEWLINE
6.162 + n = node[0][0]
6.163 + if n != token.NEWLINE:
6.164 + return self.com_stmt(node[0])
6.165 +
6.166 + return Pass()
6.167 +
6.168 + def file_input(self, nodelist):
6.169 + doc = self.get_docstring(nodelist, symbol.file_input)
6.170 + if doc is not None:
6.171 + i = 1
6.172 + else:
6.173 + i = 0
6.174 + stmts = []
6.175 + for node in nodelist[i:]:
6.176 + if node[0] != token.ENDMARKER and node[0] != token.NEWLINE:
6.177 + self.com_append_stmt(stmts, node)
6.178 + return Module(doc, Stmt(stmts))
6.179 +
6.180 + def eval_input(self, nodelist):
6.181 + # from the built-in function input()
6.182 + ### is this sufficient?
6.183 + return Expression(self.com_node(nodelist[0]))
6.184 +
6.185 + def decorator_name(self, nodelist):
6.186 + listlen = len(nodelist)
6.187 + assert listlen >= 1 and listlen % 2 == 1
6.188 +
6.189 + item = self.atom_name(nodelist)
6.190 + i = 1
6.191 + while i < listlen:
6.192 + assert nodelist[i][0] == token.DOT
6.193 + assert nodelist[i + 1][0] == token.NAME
6.194 + item = Getattr(item, nodelist[i + 1][1])
6.195 + i += 2
6.196 +
6.197 + return item
6.198 +
6.199 + def decorator(self, nodelist):
6.200 + # '@' dotted_name [ '(' [arglist] ')' ]
6.201 + assert len(nodelist) in (3, 5, 6)
6.202 + assert nodelist[0][0] == token.AT
6.203 + assert nodelist[-1][0] == token.NEWLINE
6.204 +
6.205 + assert nodelist[1][0] == symbol.dotted_name
6.206 + funcname = self.decorator_name(nodelist[1][1:])
6.207 +
6.208 + if len(nodelist) > 3:
6.209 + assert nodelist[2][0] == token.LPAR
6.210 + expr = self.com_call_function(funcname, nodelist[3])
6.211 + else:
6.212 + expr = funcname
6.213 +
6.214 + return expr
6.215 +
6.216 + def decorators(self, nodelist):
6.217 + # decorators: decorator ([NEWLINE] decorator)* NEWLINE
6.218 + items = []
6.219 + for dec_nodelist in nodelist:
6.220 + assert dec_nodelist[0] == symbol.decorator
6.221 + items.append(self.decorator(dec_nodelist[1:]))
6.222 + return Decorators(items)
6.223 +
6.224 + def decorated(self, nodelist):
6.225 + assert nodelist[0][0] == symbol.decorators
6.226 + if nodelist[1][0] == symbol.funcdef:
6.227 + n = [nodelist[0]] + list(nodelist[1][1:])
6.228 + return self.funcdef(n)
6.229 + elif nodelist[1][0] == symbol.classdef:
6.230 + decorators = self.decorators(nodelist[0][1:])
6.231 + cls = self.classdef(nodelist[1][1:])
6.232 + cls.decorators = decorators
6.233 + return cls
6.234 + raise WalkerError()
6.235 +
6.236 + def funcdef(self, nodelist):
6.237 + # -6 -5 -4 -3 -2 -1
6.238 + # funcdef: [decorators] 'def' NAME parameters ':' suite
6.239 + # parameters: '(' [varargslist] ')'
6.240 +
6.241 + if len(nodelist) == 6:
6.242 + assert nodelist[0][0] == symbol.decorators
6.243 + decorators = self.decorators(nodelist[0][1:])
6.244 + else:
6.245 + assert len(nodelist) == 5
6.246 + decorators = None
6.247 +
6.248 + lineno = nodelist[-4][2]
6.249 + name = nodelist[-4][1]
6.250 + args = nodelist[-3][2]
6.251 +
6.252 + if args[0] == symbol.varargslist:
6.253 + names, defaults, flags = self.com_arglist(args[1:])
6.254 + else:
6.255 + names = defaults = ()
6.256 + flags = 0
6.257 + doc = self.get_docstring(nodelist[-1])
6.258 +
6.259 + # code for function
6.260 + code = self.com_node(nodelist[-1])
6.261 +
6.262 + if doc is not None:
6.263 + assert isinstance(code, Stmt)
6.264 + assert isinstance(code.nodes[0], Discard)
6.265 + del code.nodes[0]
6.266 + return Function(decorators, name, names, defaults, flags, doc, code,
6.267 + lineno=lineno)
6.268 +
6.269 + def lambdef(self, nodelist):
6.270 + # lambdef: 'lambda' [varargslist] ':' test
6.271 + if nodelist[2][0] == symbol.varargslist:
6.272 + names, defaults, flags = self.com_arglist(nodelist[2][1:])
6.273 + else:
6.274 + names = defaults = ()
6.275 + flags = 0
6.276 +
6.277 + # code for lambda
6.278 + code = self.com_node(nodelist[-1])
6.279 +
6.280 + return Lambda(names, defaults, flags, code, lineno=nodelist[1][2])
6.281 + old_lambdef = lambdef
6.282 +
6.283 + def classdef(self, nodelist):
6.284 + # classdef: 'class' NAME ['(' [testlist] ')'] ':' suite
6.285 +
6.286 + name = nodelist[1][1]
6.287 + doc = self.get_docstring(nodelist[-1])
6.288 + if nodelist[2][0] == token.COLON:
6.289 + bases = []
6.290 + elif nodelist[3][0] == token.RPAR:
6.291 + bases = []
6.292 + else:
6.293 + bases = self.com_bases(nodelist[3])
6.294 +
6.295 + # code for class
6.296 + code = self.com_node(nodelist[-1])
6.297 +
6.298 + if doc is not None:
6.299 + assert isinstance(code, Stmt)
6.300 + assert isinstance(code.nodes[0], Discard)
6.301 + del code.nodes[0]
6.302 +
6.303 + return Class(name, bases, doc, code, lineno=nodelist[1][2])
6.304 +
6.305 + def stmt(self, nodelist):
6.306 + return self.com_stmt(nodelist[0])
6.307 +
6.308 + small_stmt = stmt
6.309 + flow_stmt = stmt
6.310 + compound_stmt = stmt
6.311 +
6.312 + def simple_stmt(self, nodelist):
6.313 + # small_stmt (';' small_stmt)* [';'] NEWLINE
6.314 + stmts = []
6.315 + for i in range(0, len(nodelist), 2):
6.316 + self.com_append_stmt(stmts, nodelist[i])
6.317 + return Stmt(stmts)
6.318 +
6.319 + def parameters(self, nodelist):
6.320 + raise WalkerError
6.321 +
6.322 + def varargslist(self, nodelist):
6.323 + raise WalkerError
6.324 +
6.325 + def fpdef(self, nodelist):
6.326 + raise WalkerError
6.327 +
6.328 + def fplist(self, nodelist):
6.329 + raise WalkerError
6.330 +
6.331 + def dotted_name(self, nodelist):
6.332 + raise WalkerError
6.333 +
6.334 + def comp_op(self, nodelist):
6.335 + raise WalkerError
6.336 +
6.337 + def trailer(self, nodelist):
6.338 + raise WalkerError
6.339 +
6.340 + def sliceop(self, nodelist):
6.341 + raise WalkerError
6.342 +
6.343 + def argument(self, nodelist):
6.344 + raise WalkerError
6.345 +
6.346 + # --------------------------------------------------------------
6.347 + #
6.348 + # STATEMENT NODES (invoked by com_node())
6.349 + #
6.350 +
6.351 + def expr_stmt(self, nodelist):
6.352 + # augassign testlist | testlist ('=' testlist)*
6.353 + en = nodelist[-1]
6.354 + exprNode = self.lookup_node(en)(en[1:])
6.355 + if len(nodelist) == 1:
6.356 + return Discard(exprNode, lineno=exprNode.lineno)
6.357 + if nodelist[1][0] == token.EQUAL:
6.358 + nodesl = []
6.359 + for i in range(0, len(nodelist) - 2, 2):
6.360 + nodesl.append(self.com_assign(nodelist[i], OP_ASSIGN))
6.361 + return Assign(nodesl, exprNode, lineno=nodelist[1][2])
6.362 + else:
6.363 + lval = self.com_augassign(nodelist[0])
6.364 + op = self.com_augassign_op(nodelist[1])
6.365 + return AugAssign(lval, op[1], exprNode, lineno=op[2])
6.366 + raise WalkerError, "can't get here"
6.367 +
6.368 + def print_stmt(self, nodelist):
6.369 + # print ([ test (',' test)* [','] ] | '>>' test [ (',' test)+ [','] ])
6.370 + items = []
6.371 + if len(nodelist) == 1:
6.372 + start = 1
6.373 + dest = None
6.374 + elif nodelist[1][0] == token.RIGHTSHIFT:
6.375 + assert len(nodelist) == 3 \
6.376 + or nodelist[3][0] == token.COMMA
6.377 + dest = self.com_node(nodelist[2])
6.378 + start = 4
6.379 + else:
6.380 + dest = None
6.381 + start = 1
6.382 + for i in range(start, len(nodelist), 2):
6.383 + items.append(self.com_node(nodelist[i]))
6.384 + if nodelist[-1][0] == token.COMMA:
6.385 + return Print(items, dest, lineno=nodelist[0][2])
6.386 + return Printnl(items, dest, lineno=nodelist[0][2])
6.387 +
6.388 + def del_stmt(self, nodelist):
6.389 + return self.com_assign(nodelist[1], OP_DELETE)
6.390 +
6.391 + def pass_stmt(self, nodelist):
6.392 + return Pass(lineno=nodelist[0][2])
6.393 +
6.394 + def break_stmt(self, nodelist):
6.395 + return Break(lineno=nodelist[0][2])
6.396 +
6.397 + def continue_stmt(self, nodelist):
6.398 + return Continue(lineno=nodelist[0][2])
6.399 +
6.400 + def return_stmt(self, nodelist):
6.401 + # return: [testlist]
6.402 + if len(nodelist) < 2:
6.403 + return Return(Const(None), lineno=nodelist[0][2])
6.404 + return Return(self.com_node(nodelist[1]), lineno=nodelist[0][2])
6.405 +
6.406 + def yield_stmt(self, nodelist):
6.407 + expr = self.com_node(nodelist[0])
6.408 + return Discard(expr, lineno=expr.lineno)
6.409 +
6.410 + def yield_expr(self, nodelist):
6.411 + if len(nodelist) > 1:
6.412 + value = self.com_node(nodelist[1])
6.413 + else:
6.414 + value = Const(None)
6.415 + return Yield(value, lineno=nodelist[0][2])
6.416 +
6.417 + def raise_stmt(self, nodelist):
6.418 + # raise: [test [',' test [',' test]]]
6.419 + if len(nodelist) > 5:
6.420 + expr3 = self.com_node(nodelist[5])
6.421 + else:
6.422 + expr3 = None
6.423 + if len(nodelist) > 3:
6.424 + expr2 = self.com_node(nodelist[3])
6.425 + else:
6.426 + expr2 = None
6.427 + if len(nodelist) > 1:
6.428 + expr1 = self.com_node(nodelist[1])
6.429 + else:
6.430 + expr1 = None
6.431 + return Raise(expr1, expr2, expr3, lineno=nodelist[0][2])
6.432 +
6.433 + def import_stmt(self, nodelist):
6.434 + # import_stmt: import_name | import_from
6.435 + assert len(nodelist) == 1
6.436 + return self.com_node(nodelist[0])
6.437 +
6.438 + def import_name(self, nodelist):
6.439 + # import_name: 'import' dotted_as_names
6.440 + return Import(self.com_dotted_as_names(nodelist[1]),
6.441 + lineno=nodelist[0][2])
6.442 +
6.443 + def import_from(self, nodelist):
6.444 + # import_from: 'from' ('.'* dotted_name | '.') 'import' ('*' |
6.445 + # '(' import_as_names ')' | import_as_names)
6.446 + assert nodelist[0][1] == 'from'
6.447 + idx = 1
6.448 + while nodelist[idx][1] == '.':
6.449 + idx += 1
6.450 + level = idx - 1
6.451 + if nodelist[idx][0] == symbol.dotted_name:
6.452 + fromname = self.com_dotted_name(nodelist[idx])
6.453 + idx += 1
6.454 + else:
6.455 + fromname = ""
6.456 + assert nodelist[idx][1] == 'import'
6.457 + if nodelist[idx + 1][0] == token.STAR:
6.458 + return From(fromname, [('*', None)], level,
6.459 + lineno=nodelist[0][2])
6.460 + else:
6.461 + node = nodelist[idx + 1 + (nodelist[idx + 1][0] == token.LPAR)]
6.462 + return From(fromname, self.com_import_as_names(node), level,
6.463 + lineno=nodelist[0][2])
6.464 +
6.465 + def global_stmt(self, nodelist):
6.466 + # global: NAME (',' NAME)*
6.467 + names = []
6.468 + for i in range(1, len(nodelist), 2):
6.469 + names.append(nodelist[i][1])
6.470 + return Global(names, lineno=nodelist[0][2])
6.471 +
6.472 + def exec_stmt(self, nodelist):
6.473 + # exec_stmt: 'exec' expr ['in' expr [',' expr]]
6.474 + expr1 = self.com_node(nodelist[1])
6.475 + if len(nodelist) >= 4:
6.476 + expr2 = self.com_node(nodelist[3])
6.477 + if len(nodelist) >= 6:
6.478 + expr3 = self.com_node(nodelist[5])
6.479 + else:
6.480 + expr3 = None
6.481 + else:
6.482 + expr2 = expr3 = None
6.483 +
6.484 + return Exec(expr1, expr2, expr3, lineno=nodelist[0][2])
6.485 +
6.486 + def assert_stmt(self, nodelist):
6.487 + # 'assert': test, [',' test]
6.488 + expr1 = self.com_node(nodelist[1])
6.489 + if (len(nodelist) == 4):
6.490 + expr2 = self.com_node(nodelist[3])
6.491 + else:
6.492 + expr2 = None
6.493 + return Assert(expr1, expr2, lineno=nodelist[0][2])
6.494 +
6.495 + def if_stmt(self, nodelist):
6.496 + # if: test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
6.497 + tests = []
6.498 + for i in range(0, len(nodelist) - 3, 4):
6.499 + testNode = self.com_node(nodelist[i + 1])
6.500 + suiteNode = self.com_node(nodelist[i + 3])
6.501 + tests.append((testNode, suiteNode))
6.502 +
6.503 + if len(nodelist) % 4 == 3:
6.504 + elseNode = self.com_node(nodelist[-1])
6.505 +## elseNode.lineno = nodelist[-1][1][2]
6.506 + else:
6.507 + elseNode = None
6.508 + return If(tests, elseNode, lineno=nodelist[0][2])
6.509 +
6.510 + def while_stmt(self, nodelist):
6.511 + # 'while' test ':' suite ['else' ':' suite]
6.512 +
6.513 + testNode = self.com_node(nodelist[1])
6.514 + bodyNode = self.com_node(nodelist[3])
6.515 +
6.516 + if len(nodelist) > 4:
6.517 + elseNode = self.com_node(nodelist[6])
6.518 + else:
6.519 + elseNode = None
6.520 +
6.521 + return While(testNode, bodyNode, elseNode, lineno=nodelist[0][2])
6.522 +
6.523 + def for_stmt(self, nodelist):
6.524 + # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
6.525 +
6.526 + assignNode = self.com_assign(nodelist[1], OP_ASSIGN)
6.527 + listNode = self.com_node(nodelist[3])
6.528 + bodyNode = self.com_node(nodelist[5])
6.529 +
6.530 + if len(nodelist) > 8:
6.531 + elseNode = self.com_node(nodelist[8])
6.532 + else:
6.533 + elseNode = None
6.534 +
6.535 + return For(assignNode, listNode, bodyNode, elseNode,
6.536 + lineno=nodelist[0][2])
6.537 +
6.538 + def try_stmt(self, nodelist):
6.539 + return self.com_try_except_finally(nodelist)
6.540 +
6.541 + def with_stmt(self, nodelist):
6.542 + return self.com_with(nodelist)
6.543 +
6.544 + def suite(self, nodelist):
6.545 + # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
6.546 + if len(nodelist) == 1:
6.547 + return self.com_stmt(nodelist[0])
6.548 +
6.549 + stmts = []
6.550 + for node in nodelist:
6.551 + if node[0] == symbol.stmt:
6.552 + self.com_append_stmt(stmts, node)
6.553 + return Stmt(stmts)
6.554 +
6.555 + # --------------------------------------------------------------
6.556 + #
6.557 + # EXPRESSION NODES (invoked by com_node())
6.558 + #
6.559 +
6.560 + def testlist(self, nodelist):
6.561 + # testlist: expr (',' expr)* [',']
6.562 + # testlist_safe: test [(',' test)+ [',']]
6.563 + # exprlist: expr (',' expr)* [',']
6.564 + return self.com_binary(Tuple, nodelist)
6.565 +
6.566 + testlist_safe = testlist # XXX
6.567 + testlist1 = testlist
6.568 + exprlist = testlist
6.569 +
6.570 + def testlist_comp(self, nodelist):
6.571 + # test ( comp_for | (',' test)* [','] )
6.572 + assert nodelist[0][0] == symbol.test
6.573 + if len(nodelist) == 2 and nodelist[1][0] == symbol.comp_for:
6.574 + test = self.com_node(nodelist[0])
6.575 + return self.com_generator_expression(test, nodelist[1])
6.576 + return self.testlist(nodelist)
6.577 +
6.578 + def test(self, nodelist):
6.579 + # or_test ['if' or_test 'else' test] | lambdef
6.580 + if len(nodelist) == 1 and nodelist[0][0] == symbol.lambdef:
6.581 + return self.lambdef(nodelist[0])
6.582 + then = self.com_node(nodelist[0])
6.583 + if len(nodelist) > 1:
6.584 + assert len(nodelist) == 5
6.585 + assert nodelist[1][1] == 'if'
6.586 + assert nodelist[3][1] == 'else'
6.587 + test = self.com_node(nodelist[2])
6.588 + else_ = self.com_node(nodelist[4])
6.589 + return IfExp(test, then, else_, lineno=nodelist[1][2])
6.590 + return then
6.591 +
6.592 + def or_test(self, nodelist):
6.593 + # and_test ('or' and_test)* | lambdef
6.594 + if len(nodelist) == 1 and nodelist[0][0] == symbol.lambdef:
6.595 + return self.lambdef(nodelist[0])
6.596 + return self.com_binary(Or, nodelist)
6.597 + old_test = or_test
6.598 +
6.599 + def and_test(self, nodelist):
6.600 + # not_test ('and' not_test)*
6.601 + return self.com_binary(And, nodelist)
6.602 +
6.603 + def not_test(self, nodelist):
6.604 + # 'not' not_test | comparison
6.605 + result = self.com_node(nodelist[-1])
6.606 + if len(nodelist) == 2:
6.607 + return Not(result, lineno=nodelist[0][2])
6.608 + return result
6.609 +
6.610 + def comparison(self, nodelist):
6.611 + # comparison: expr (comp_op expr)*
6.612 + node = self.com_node(nodelist[0])
6.613 + if len(nodelist) == 1:
6.614 + return node
6.615 +
6.616 + results = []
6.617 + for i in range(2, len(nodelist), 2):
6.618 + nl = nodelist[i-1]
6.619 +
6.620 + # comp_op: '<' | '>' | '=' | '>=' | '<=' | '<>' | '!=' | '=='
6.621 + # | 'in' | 'not' 'in' | 'is' | 'is' 'not'
6.622 + n = nl[1]
6.623 + if n[0] == token.NAME:
6.624 + type = n[1]
6.625 + if len(nl) == 3:
6.626 + if type == 'not':
6.627 + type = 'not in'
6.628 + else:
6.629 + type = 'is not'
6.630 + else:
6.631 + type = _cmp_types[n[0]]
6.632 +
6.633 + lineno = nl[1][2]
6.634 + results.append((type, self.com_node(nodelist[i])))
6.635 +
6.636 + # we need a special "compare" node so that we can distinguish
6.637 + # 3 < x < 5 from (3 < x) < 5
6.638 + # the two have very different semantics and results (note that the
6.639 + # latter form is always true)
6.640 +
6.641 + return Compare(node, results, lineno=lineno)
6.642 +
6.643 + def expr(self, nodelist):
6.644 + # xor_expr ('|' xor_expr)*
6.645 + return self.com_binary(Bitor, nodelist)
6.646 +
6.647 + def xor_expr(self, nodelist):
6.648 + # xor_expr ('^' xor_expr)*
6.649 + return self.com_binary(Bitxor, nodelist)
6.650 +
6.651 + def and_expr(self, nodelist):
6.652 + # xor_expr ('&' xor_expr)*
6.653 + return self.com_binary(Bitand, nodelist)
6.654 +
6.655 + def shift_expr(self, nodelist):
6.656 + # shift_expr ('<<'|'>>' shift_expr)*
6.657 + node = self.com_node(nodelist[0])
6.658 + for i in range(2, len(nodelist), 2):
6.659 + right = self.com_node(nodelist[i])
6.660 + if nodelist[i-1][0] == token.LEFTSHIFT:
6.661 + node = LeftShift([node, right], lineno=nodelist[1][2])
6.662 + elif nodelist[i-1][0] == token.RIGHTSHIFT:
6.663 + node = RightShift([node, right], lineno=nodelist[1][2])
6.664 + else:
6.665 + raise ValueError, "unexpected token: %s" % nodelist[i-1][0]
6.666 + return node
6.667 +
6.668 + def arith_expr(self, nodelist):
6.669 + node = self.com_node(nodelist[0])
6.670 + for i in range(2, len(nodelist), 2):
6.671 + right = self.com_node(nodelist[i])
6.672 + if nodelist[i-1][0] == token.PLUS:
6.673 + node = Add([node, right], lineno=nodelist[1][2])
6.674 + elif nodelist[i-1][0] == token.MINUS:
6.675 + node = Sub([node, right], lineno=nodelist[1][2])
6.676 + else:
6.677 + raise ValueError, "unexpected token: %s" % nodelist[i-1][0]
6.678 + return node
6.679 +
6.680 + def term(self, nodelist):
6.681 + node = self.com_node(nodelist[0])
6.682 + for i in range(2, len(nodelist), 2):
6.683 + right = self.com_node(nodelist[i])
6.684 + t = nodelist[i-1][0]
6.685 + if t == token.STAR:
6.686 + node = Mul([node, right])
6.687 + elif t == token.SLASH:
6.688 + node = Div([node, right])
6.689 + elif t == token.PERCENT:
6.690 + node = Mod([node, right])
6.691 + elif t == token.DOUBLESLASH:
6.692 + node = FloorDiv([node, right])
6.693 + else:
6.694 + raise ValueError, "unexpected token: %s" % t
6.695 + node.lineno = nodelist[1][2]
6.696 + return node
6.697 +
6.698 + def factor(self, nodelist):
6.699 + elt = nodelist[0]
6.700 + t = elt[0]
6.701 + node = self.lookup_node(nodelist[-1])(nodelist[-1][1:])
6.702 + # need to handle (unary op)constant here...
6.703 + if t == token.PLUS:
6.704 + return UnaryAdd(node, lineno=elt[2])
6.705 + elif t == token.MINUS:
6.706 + return UnarySub(node, lineno=elt[2])
6.707 + elif t == token.TILDE:
6.708 + node = Invert(node, lineno=elt[2])
6.709 + return node
6.710 +
6.711 + def power(self, nodelist):
6.712 + # power: atom trailer* ('**' factor)*
6.713 + node = self.com_node(nodelist[0])
6.714 + for i in range(1, len(nodelist)):
6.715 + elt = nodelist[i]
6.716 + if elt[0] == token.DOUBLESTAR:
6.717 + return Power([node, self.com_node(nodelist[i+1])],
6.718 + lineno=elt[2])
6.719 +
6.720 + node = self.com_apply_trailer(node, elt)
6.721 +
6.722 + return node
6.723 +
6.724 + def atom(self, nodelist):
6.725 + return self._atom_dispatch[nodelist[0][0]](nodelist)
6.726 +
6.727 + def atom_lpar(self, nodelist):
6.728 + if nodelist[1][0] == token.RPAR:
6.729 + return Tuple((), lineno=nodelist[0][2])
6.730 + return self.com_node(nodelist[1])
6.731 +
6.732 + def atom_lsqb(self, nodelist):
6.733 + if nodelist[1][0] == token.RSQB:
6.734 + return List((), lineno=nodelist[0][2])
6.735 + return self.com_list_constructor(nodelist[1])
6.736 +
6.737 + def atom_lbrace(self, nodelist):
6.738 + if nodelist[1][0] == token.RBRACE:
6.739 + return Dict((), lineno=nodelist[0][2])
6.740 + return self.com_dictorsetmaker(nodelist[1])
6.741 +
6.742 + def atom_backquote(self, nodelist):
6.743 + return Backquote(self.com_node(nodelist[1]))
6.744 +
6.745 + def atom_number(self, nodelist):
6.746 + ### need to verify this matches compile.c
6.747 + k = eval(nodelist[0][1])
6.748 + return Const(k, lineno=nodelist[0][2])
6.749 +
6.750 + def decode_literal(self, lit):
6.751 + if self.encoding:
6.752 + # this is particularly fragile & a bit of a
6.753 + # hack... changes in compile.c:parsestr and
6.754 + # tokenizer.c must be reflected here.
6.755 + if self.encoding not in ['utf-8', 'iso-8859-1']:
6.756 + lit = unicode(lit, 'utf-8').encode(self.encoding)
6.757 + return eval("# coding: %s\n%s" % (self.encoding, lit))
6.758 + else:
6.759 + return eval(lit)
6.760 +
6.761 + def atom_string(self, nodelist):
6.762 + k = ''
6.763 + for node in nodelist:
6.764 + k += self.decode_literal(node[1])
6.765 + return Const(k, lineno=nodelist[0][2])
6.766 +
6.767 + def atom_name(self, nodelist):
6.768 + return Name(nodelist[0][1], lineno=nodelist[0][2])
6.769 +
6.770 + # --------------------------------------------------------------
6.771 + #
6.772 + # INTERNAL PARSING UTILITIES
6.773 + #
6.774 +
6.775 + # The use of com_node() introduces a lot of extra stack frames,
6.776 + # enough to cause a stack overflow compiling test.test_parser with
6.777 + # the standard interpreter recursionlimit. The com_node() is a
6.778 + # convenience function that hides the dispatch details, but comes
6.779 + # at a very high cost. It is more efficient to dispatch directly
6.780 + # in the callers. In these cases, use lookup_node() and call the
6.781 + # dispatched node directly.
6.782 +
6.783 + def lookup_node(self, node):
6.784 + return self._dispatch[node[0]]
6.785 +
6.786 + def com_node(self, node):
6.787 + # Note: compile.c has handling in com_node for del_stmt, pass_stmt,
6.788 + # break_stmt, stmt, small_stmt, flow_stmt, simple_stmt,
6.789 + # and compound_stmt.
6.790 + # We'll just dispatch them.
6.791 + return self._dispatch[node[0]](node[1:])
6.792 +
6.793 + def com_NEWLINE(self, *args):
6.794 + # A ';' at the end of a line can make a NEWLINE token appear
6.795 + # here, Render it harmless. (genc discards ('discard',
6.796 + # ('const', xxxx)) Nodes)
6.797 + return Discard(Const(None))
6.798 +
6.799 + def com_arglist(self, nodelist):
6.800 + # varargslist:
6.801 + # (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME)
6.802 + # | fpdef ['=' test] (',' fpdef ['=' test])* [',']
6.803 + # fpdef: NAME | '(' fplist ')'
6.804 + # fplist: fpdef (',' fpdef)* [',']
6.805 + names = []
6.806 + defaults = []
6.807 + flags = 0
6.808 +
6.809 + i = 0
6.810 + while i < len(nodelist):
6.811 + node = nodelist[i]
6.812 + if node[0] == token.STAR or node[0] == token.DOUBLESTAR:
6.813 + if node[0] == token.STAR:
6.814 + node = nodelist[i+1]
6.815 + if node[0] == token.NAME:
6.816 + names.append(node[1])
6.817 + flags = flags | CO_VARARGS
6.818 + i = i + 3
6.819 +
6.820 + if i < len(nodelist):
6.821 + # should be DOUBLESTAR
6.822 + t = nodelist[i][0]
6.823 + if t == token.DOUBLESTAR:
6.824 + node = nodelist[i+1]
6.825 + else:
6.826 + raise ValueError, "unexpected token: %s" % t
6.827 + names.append(node[1])
6.828 + flags = flags | CO_VARKEYWORDS
6.829 +
6.830 + break
6.831 +
6.832 + # fpdef: NAME | '(' fplist ')'
6.833 + names.append(self.com_fpdef(node))
6.834 +
6.835 + i = i + 1
6.836 + if i < len(nodelist) and nodelist[i][0] == token.EQUAL:
6.837 + defaults.append(self.com_node(nodelist[i + 1]))
6.838 + i = i + 2
6.839 + elif len(defaults):
6.840 + # we have already seen an argument with default, but here
6.841 + # came one without
6.842 + raise SyntaxError, "non-default argument follows default argument"
6.843 +
6.844 + # skip the comma
6.845 + i = i + 1
6.846 +
6.847 + return names, defaults, flags
6.848 +
6.849 + def com_fpdef(self, node):
6.850 + # fpdef: NAME | '(' fplist ')'
6.851 + if node[1][0] == token.LPAR:
6.852 + return self.com_fplist(node[2])
6.853 + return node[1][1]
6.854 +
6.855 + def com_fplist(self, node):
6.856 + # fplist: fpdef (',' fpdef)* [',']
6.857 + if len(node) == 2:
6.858 + return self.com_fpdef(node[1])
6.859 + list = []
6.860 + for i in range(1, len(node), 2):
6.861 + list.append(self.com_fpdef(node[i]))
6.862 + return tuple(list)
6.863 +
6.864 + def com_dotted_name(self, node):
6.865 + # String together the dotted names and return the string
6.866 + name = ""
6.867 + for n in node:
6.868 + if type(n) == type(()) and n[0] == 1:
6.869 + name = name + n[1] + '.'
6.870 + return name[:-1]
6.871 +
6.872 + def com_dotted_as_name(self, node):
6.873 + assert node[0] == symbol.dotted_as_name
6.874 + node = node[1:]
6.875 + dot = self.com_dotted_name(node[0][1:])
6.876 + if len(node) == 1:
6.877 + return dot, None
6.878 + assert node[1][1] == 'as'
6.879 + assert node[2][0] == token.NAME
6.880 + return dot, node[2][1]
6.881 +
6.882 + def com_dotted_as_names(self, node):
6.883 + assert node[0] == symbol.dotted_as_names
6.884 + node = node[1:]
6.885 + names = [self.com_dotted_as_name(node[0])]
6.886 + for i in range(2, len(node), 2):
6.887 + names.append(self.com_dotted_as_name(node[i]))
6.888 + return names
6.889 +
6.890 + def com_import_as_name(self, node):
6.891 + assert node[0] == symbol.import_as_name
6.892 + node = node[1:]
6.893 + assert node[0][0] == token.NAME
6.894 + if len(node) == 1:
6.895 + return node[0][1], None
6.896 + assert node[1][1] == 'as', node
6.897 + assert node[2][0] == token.NAME
6.898 + return node[0][1], node[2][1]
6.899 +
6.900 + def com_import_as_names(self, node):
6.901 + assert node[0] == symbol.import_as_names
6.902 + node = node[1:]
6.903 + names = [self.com_import_as_name(node[0])]
6.904 + for i in range(2, len(node), 2):
6.905 + names.append(self.com_import_as_name(node[i]))
6.906 + return names
6.907 +
6.908 + def com_bases(self, node):
6.909 + bases = []
6.910 + for i in range(1, len(node), 2):
6.911 + bases.append(self.com_node(node[i]))
6.912 + return bases
6.913 +
6.914 + def com_try_except_finally(self, nodelist):
6.915 + # ('try' ':' suite
6.916 + # ((except_clause ':' suite)+ ['else' ':' suite] ['finally' ':' suite]
6.917 + # | 'finally' ':' suite))
6.918 +
6.919 + if nodelist[3][0] == token.NAME:
6.920 + # first clause is a finally clause: only try-finally
6.921 + return TryFinally(self.com_node(nodelist[2]),
6.922 + self.com_node(nodelist[5]),
6.923 + lineno=nodelist[0][2])
6.924 +
6.925 + #tryexcept: [TryNode, [except_clauses], elseNode)]
6.926 + clauses = []
6.927 + elseNode = None
6.928 + finallyNode = None
6.929 + for i in range(3, len(nodelist), 3):
6.930 + node = nodelist[i]
6.931 + if node[0] == symbol.except_clause:
6.932 + # except_clause: 'except' [expr [(',' | 'as') expr]] */
6.933 + if len(node) > 2:
6.934 + expr1 = self.com_node(node[2])
6.935 + if len(node) > 4:
6.936 + expr2 = self.com_assign(node[4], OP_ASSIGN)
6.937 + else:
6.938 + expr2 = None
6.939 + else:
6.940 + expr1 = expr2 = None
6.941 + clauses.append((expr1, expr2, self.com_node(nodelist[i+2])))
6.942 +
6.943 + if node[0] == token.NAME:
6.944 + if node[1] == 'else':
6.945 + elseNode = self.com_node(nodelist[i+2])
6.946 + elif node[1] == 'finally':
6.947 + finallyNode = self.com_node(nodelist[i+2])
6.948 + try_except = TryExcept(self.com_node(nodelist[2]), clauses, elseNode,
6.949 + lineno=nodelist[0][2])
6.950 + if finallyNode:
6.951 + return TryFinally(try_except, finallyNode, lineno=nodelist[0][2])
6.952 + else:
6.953 + return try_except
6.954 +
6.955 + def com_with(self, nodelist):
6.956 + # with_stmt: 'with' with_item (',' with_item)* ':' suite
6.957 + body = self.com_node(nodelist[-1])
6.958 + for i in range(len(nodelist) - 3, 0, -2):
6.959 + ret = self.com_with_item(nodelist[i], body, nodelist[0][2])
6.960 + if i == 1:
6.961 + return ret
6.962 + body = ret
6.963 +
6.964 + def com_with_item(self, nodelist, body, lineno):
6.965 + # with_item: test ['as' expr]
6.966 + if len(nodelist) == 4:
6.967 + var = self.com_assign(nodelist[3], OP_ASSIGN)
6.968 + else:
6.969 + var = None
6.970 + expr = self.com_node(nodelist[1])
6.971 + return With(expr, var, body, lineno=lineno)
6.972 +
6.973 + def com_augassign_op(self, node):
6.974 + assert node[0] == symbol.augassign
6.975 + return node[1]
6.976 +
6.977 + def com_augassign(self, node):
6.978 + """Return node suitable for lvalue of augmented assignment
6.979 +
6.980 + Names, slices, and attributes are the only allowable nodes.
6.981 + """
6.982 + l = self.com_node(node)
6.983 + if l.__class__ in (Name, Slice, Subscript, Getattr):
6.984 + return l
6.985 + raise SyntaxError, "can't assign to %s" % l.__class__.__name__
6.986 +
6.987 + def com_assign(self, node, assigning):
6.988 + # return a node suitable for use as an "lvalue"
6.989 + # loop to avoid trivial recursion
6.990 + while 1:
6.991 + t = node[0]
6.992 + if t in (symbol.exprlist, symbol.testlist, symbol.testlist_safe, symbol.testlist_comp):
6.993 + if len(node) > 2:
6.994 + return self.com_assign_tuple(node, assigning)
6.995 + node = node[1]
6.996 + elif t in _assign_types:
6.997 + if len(node) > 2:
6.998 + raise SyntaxError, "can't assign to operator"
6.999 + node = node[1]
6.1000 + elif t == symbol.power:
6.1001 + if node[1][0] != symbol.atom:
6.1002 + raise SyntaxError, "can't assign to operator"
6.1003 + if len(node) > 2:
6.1004 + primary = self.com_node(node[1])
6.1005 + for i in range(2, len(node)-1):
6.1006 + ch = node[i]
6.1007 + if ch[0] == token.DOUBLESTAR:
6.1008 + raise SyntaxError, "can't assign to operator"
6.1009 + primary = self.com_apply_trailer(primary, ch)
6.1010 + return self.com_assign_trailer(primary, node[-1],
6.1011 + assigning)
6.1012 + node = node[1]
6.1013 + elif t == symbol.atom:
6.1014 + t = node[1][0]
6.1015 + if t == token.LPAR:
6.1016 + node = node[2]
6.1017 + if node[0] == token.RPAR:
6.1018 + raise SyntaxError, "can't assign to ()"
6.1019 + elif t == token.LSQB:
6.1020 + node = node[2]
6.1021 + if node[0] == token.RSQB:
6.1022 + raise SyntaxError, "can't assign to []"
6.1023 + return self.com_assign_list(node, assigning)
6.1024 + elif t == token.NAME:
6.1025 + return self.com_assign_name(node[1], assigning)
6.1026 + else:
6.1027 + raise SyntaxError, "can't assign to literal"
6.1028 + else:
6.1029 + raise SyntaxError, "bad assignment (%s)" % t
6.1030 +
6.1031 + def com_assign_tuple(self, node, assigning):
6.1032 + assigns = []
6.1033 + for i in range(1, len(node), 2):
6.1034 + assigns.append(self.com_assign(node[i], assigning))
6.1035 + return AssTuple(assigns, lineno=extractLineNo(node))
6.1036 +
6.1037 + def com_assign_list(self, node, assigning):
6.1038 + assigns = []
6.1039 + for i in range(1, len(node), 2):
6.1040 + if i + 1 < len(node):
6.1041 + if node[i + 1][0] == symbol.list_for:
6.1042 + raise SyntaxError, "can't assign to list comprehension"
6.1043 + assert node[i + 1][0] == token.COMMA, node[i + 1]
6.1044 + assigns.append(self.com_assign(node[i], assigning))
6.1045 + return AssList(assigns, lineno=extractLineNo(node))
6.1046 +
6.1047 + def com_assign_name(self, node, assigning):
6.1048 + return AssName(node[1], assigning, lineno=node[2])
6.1049 +
6.1050 + def com_assign_trailer(self, primary, node, assigning):
6.1051 + t = node[1][0]
6.1052 + if t == token.DOT:
6.1053 + return self.com_assign_attr(primary, node[2], assigning)
6.1054 + if t == token.LSQB:
6.1055 + return self.com_subscriptlist(primary, node[2], assigning)
6.1056 + if t == token.LPAR:
6.1057 + raise SyntaxError, "can't assign to function call"
6.1058 + raise SyntaxError, "unknown trailer type: %s" % t
6.1059 +
6.1060 + def com_assign_attr(self, primary, node, assigning):
6.1061 + return AssAttr(primary, node[1], assigning, lineno=node[-1])
6.1062 +
6.1063 + def com_binary(self, constructor, nodelist):
6.1064 + "Compile 'NODE (OP NODE)*' into (type, [ node1, ..., nodeN ])."
6.1065 + l = len(nodelist)
6.1066 + if l == 1:
6.1067 + n = nodelist[0]
6.1068 + return self.lookup_node(n)(n[1:])
6.1069 + items = []
6.1070 + for i in range(0, l, 2):
6.1071 + n = nodelist[i]
6.1072 + items.append(self.lookup_node(n)(n[1:]))
6.1073 + return constructor(items, lineno=extractLineNo(nodelist))
6.1074 +
6.1075 + def com_stmt(self, node):
6.1076 + result = self.lookup_node(node)(node[1:])
6.1077 + assert result is not None
6.1078 + if isinstance(result, Stmt):
6.1079 + return result
6.1080 + return Stmt([result])
6.1081 +
6.1082 + def com_append_stmt(self, stmts, node):
6.1083 + result = self.lookup_node(node)(node[1:])
6.1084 + assert result is not None
6.1085 + if isinstance(result, Stmt):
6.1086 + stmts.extend(result.nodes)
6.1087 + else:
6.1088 + stmts.append(result)
6.1089 +
6.1090 + def com_list_constructor(self, nodelist):
6.1091 + # listmaker: test ( list_for | (',' test)* [','] )
6.1092 + values = []
6.1093 + for i in range(1, len(nodelist)):
6.1094 + if nodelist[i][0] == symbol.list_for:
6.1095 + assert len(nodelist[i:]) == 1
6.1096 + return self.com_list_comprehension(values[0],
6.1097 + nodelist[i])
6.1098 + elif nodelist[i][0] == token.COMMA:
6.1099 + continue
6.1100 + values.append(self.com_node(nodelist[i]))
6.1101 + return List(values, lineno=values[0].lineno)
6.1102 +
6.1103 + def com_list_comprehension(self, expr, node):
6.1104 + return self.com_comprehension(expr, None, node, 'list')
6.1105 +
6.1106 + def com_comprehension(self, expr1, expr2, node, type):
6.1107 + # list_iter: list_for | list_if
6.1108 + # list_for: 'for' exprlist 'in' testlist [list_iter]
6.1109 + # list_if: 'if' test [list_iter]
6.1110 +
6.1111 + # XXX should raise SyntaxError for assignment
6.1112 + # XXX(avassalotti) Set and dict comprehensions should have generator
6.1113 + # semantics. In other words, they shouldn't leak
6.1114 + # variables outside of the comprehension's scope.
6.1115 +
6.1116 + lineno = node[1][2]
6.1117 + fors = []
6.1118 + while node:
6.1119 + t = node[1][1]
6.1120 + if t == 'for':
6.1121 + assignNode = self.com_assign(node[2], OP_ASSIGN)
6.1122 + compNode = self.com_node(node[4])
6.1123 + newfor = ListCompFor(assignNode, compNode, [])
6.1124 + newfor.lineno = node[1][2]
6.1125 + fors.append(newfor)
6.1126 + if len(node) == 5:
6.1127 + node = None
6.1128 + elif type == 'list':
6.1129 + node = self.com_list_iter(node[5])
6.1130 + else:
6.1131 + node = self.com_comp_iter(node[5])
6.1132 + elif t == 'if':
6.1133 + test = self.com_node(node[2])
6.1134 + newif = ListCompIf(test, lineno=node[1][2])
6.1135 + newfor.ifs.append(newif)
6.1136 + if len(node) == 3:
6.1137 + node = None
6.1138 + elif type == 'list':
6.1139 + node = self.com_list_iter(node[3])
6.1140 + else:
6.1141 + node = self.com_comp_iter(node[3])
6.1142 + else:
6.1143 + raise SyntaxError, \
6.1144 + ("unexpected comprehension element: %s %d"
6.1145 + % (node, lineno))
6.1146 + if type == 'list':
6.1147 + return ListComp(expr1, fors, lineno=lineno)
6.1148 + elif type == 'set':
6.1149 + return SetComp(expr1, fors, lineno=lineno)
6.1150 + elif type == 'dict':
6.1151 + return DictComp(expr1, expr2, fors, lineno=lineno)
6.1152 + else:
6.1153 + raise ValueError("unexpected comprehension type: " + repr(type))
6.1154 +
6.1155 + def com_list_iter(self, node):
6.1156 + assert node[0] == symbol.list_iter
6.1157 + return node[1]
6.1158 +
6.1159 + def com_comp_iter(self, node):
6.1160 + assert node[0] == symbol.comp_iter
6.1161 + return node[1]
6.1162 +
6.1163 + def com_generator_expression(self, expr, node):
6.1164 + # comp_iter: comp_for | comp_if
6.1165 + # comp_for: 'for' exprlist 'in' test [comp_iter]
6.1166 + # comp_if: 'if' test [comp_iter]
6.1167 +
6.1168 + lineno = node[1][2]
6.1169 + fors = []
6.1170 + while node:
6.1171 + t = node[1][1]
6.1172 + if t == 'for':
6.1173 + assignNode = self.com_assign(node[2], OP_ASSIGN)
6.1174 + genNode = self.com_node(node[4])
6.1175 + newfor = GenExprFor(assignNode, genNode, [],
6.1176 + lineno=node[1][2])
6.1177 + fors.append(newfor)
6.1178 + if (len(node)) == 5:
6.1179 + node = None
6.1180 + else:
6.1181 + node = self.com_comp_iter(node[5])
6.1182 + elif t == 'if':
6.1183 + test = self.com_node(node[2])
6.1184 + newif = GenExprIf(test, lineno=node[1][2])
6.1185 + newfor.ifs.append(newif)
6.1186 + if len(node) == 3:
6.1187 + node = None
6.1188 + else:
6.1189 + node = self.com_comp_iter(node[3])
6.1190 + else:
6.1191 + raise SyntaxError, \
6.1192 + ("unexpected generator expression element: %s %d"
6.1193 + % (node, lineno))
6.1194 + fors[0].is_outmost = True
6.1195 + return GenExpr(GenExprInner(expr, fors), lineno=lineno)
6.1196 +
6.1197 + def com_dictorsetmaker(self, nodelist):
6.1198 + # dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
6.1199 + # (test (comp_for | (',' test)* [','])) )
6.1200 + assert nodelist[0] == symbol.dictorsetmaker
6.1201 + nodelist = nodelist[1:]
6.1202 + if len(nodelist) == 1 or nodelist[1][0] == token.COMMA:
6.1203 + # set literal
6.1204 + items = []
6.1205 + for i in range(0, len(nodelist), 2):
6.1206 + items.append(self.com_node(nodelist[i]))
6.1207 + return Set(items, lineno=items[0].lineno)
6.1208 + elif nodelist[1][0] == symbol.comp_for:
6.1209 + # set comprehension
6.1210 + expr = self.com_node(nodelist[0])
6.1211 + return self.com_comprehension(expr, None, nodelist[1], 'set')
6.1212 + elif len(nodelist) > 3 and nodelist[3][0] == symbol.comp_for:
6.1213 + # dict comprehension
6.1214 + assert nodelist[1][0] == token.COLON
6.1215 + key = self.com_node(nodelist[0])
6.1216 + value = self.com_node(nodelist[2])
6.1217 + return self.com_comprehension(key, value, nodelist[3], 'dict')
6.1218 + else:
6.1219 + # dict literal
6.1220 + items = []
6.1221 + for i in range(0, len(nodelist), 4):
6.1222 + items.append((self.com_node(nodelist[i]),
6.1223 + self.com_node(nodelist[i+2])))
6.1224 + return Dict(items, lineno=items[0][0].lineno)
6.1225 +
6.1226 + def com_apply_trailer(self, primaryNode, nodelist):
6.1227 + t = nodelist[1][0]
6.1228 + if t == token.LPAR:
6.1229 + return self.com_call_function(primaryNode, nodelist[2])
6.1230 + if t == token.DOT:
6.1231 + return self.com_select_member(primaryNode, nodelist[2])
6.1232 + if t == token.LSQB:
6.1233 + return self.com_subscriptlist(primaryNode, nodelist[2], OP_APPLY)
6.1234 +
6.1235 + raise SyntaxError, 'unknown node type: %s' % t
6.1236 +
6.1237 + def com_select_member(self, primaryNode, nodelist):
6.1238 + if nodelist[0] != token.NAME:
6.1239 + raise SyntaxError, "member must be a name"
6.1240 + return Getattr(primaryNode, nodelist[1], lineno=nodelist[2])
6.1241 +
6.1242 + def com_call_function(self, primaryNode, nodelist):
6.1243 + if nodelist[0] == token.RPAR:
6.1244 + return CallFunc(primaryNode, [], lineno=extractLineNo(nodelist))
6.1245 + args = []
6.1246 + kw = 0
6.1247 + star_node = dstar_node = None
6.1248 + len_nodelist = len(nodelist)
6.1249 + i = 1
6.1250 + while i < len_nodelist:
6.1251 + node = nodelist[i]
6.1252 +
6.1253 + if node[0]==token.STAR:
6.1254 + if star_node is not None:
6.1255 + raise SyntaxError, 'already have the varargs indentifier'
6.1256 + star_node = self.com_node(nodelist[i+1])
6.1257 + i = i + 3
6.1258 + continue
6.1259 + elif node[0]==token.DOUBLESTAR:
6.1260 + if dstar_node is not None:
6.1261 + raise SyntaxError, 'already have the kwargs indentifier'
6.1262 + dstar_node = self.com_node(nodelist[i+1])
6.1263 + i = i + 3
6.1264 + continue
6.1265 +
6.1266 + # positional or named parameters
6.1267 + kw, result = self.com_argument(node, kw, star_node)
6.1268 +
6.1269 + if len_nodelist != 2 and isinstance(result, GenExpr) \
6.1270 + and len(node) == 3 and node[2][0] == symbol.comp_for:
6.1271 + # allow f(x for x in y), but reject f(x for x in y, 1)
6.1272 + # should use f((x for x in y), 1) instead of f(x for x in y, 1)
6.1273 + raise SyntaxError, 'generator expression needs parenthesis'
6.1274 +
6.1275 + args.append(result)
6.1276 + i = i + 2
6.1277 +
6.1278 + return CallFunc(primaryNode, args, star_node, dstar_node,
6.1279 + lineno=extractLineNo(nodelist))
6.1280 +
6.1281 + def com_argument(self, nodelist, kw, star_node):
6.1282 + if len(nodelist) == 3 and nodelist[2][0] == symbol.comp_for:
6.1283 + test = self.com_node(nodelist[1])
6.1284 + return 0, self.com_generator_expression(test, nodelist[2])
6.1285 + if len(nodelist) == 2:
6.1286 + if kw:
6.1287 + raise SyntaxError, "non-keyword arg after keyword arg"
6.1288 + if star_node:
6.1289 + raise SyntaxError, "only named arguments may follow *expression"
6.1290 + return 0, self.com_node(nodelist[1])
6.1291 + result = self.com_node(nodelist[3])
6.1292 + n = nodelist[1]
6.1293 + while len(n) == 2 and n[0] != token.NAME:
6.1294 + n = n[1]
6.1295 + if n[0] != token.NAME:
6.1296 + raise SyntaxError, "keyword can't be an expression (%s)"%n[0]
6.1297 + node = Keyword(n[1], result, lineno=n[2])
6.1298 + return 1, node
6.1299 +
6.1300 + def com_subscriptlist(self, primary, nodelist, assigning):
6.1301 + # slicing: simple_slicing | extended_slicing
6.1302 + # simple_slicing: primary "[" short_slice "]"
6.1303 + # extended_slicing: primary "[" slice_list "]"
6.1304 + # slice_list: slice_item ("," slice_item)* [","]
6.1305 +
6.1306 + # backwards compat slice for '[i:j]'
6.1307 + if len(nodelist) == 2:
6.1308 + sub = nodelist[1]
6.1309 + if (sub[1][0] == token.COLON or \
6.1310 + (len(sub) > 2 and sub[2][0] == token.COLON)) and \
6.1311 + sub[-1][0] != symbol.sliceop:
6.1312 + return self.com_slice(primary, sub, assigning)
6.1313 +
6.1314 + subscripts = []
6.1315 + for i in range(1, len(nodelist), 2):
6.1316 + subscripts.append(self.com_subscript(nodelist[i]))
6.1317 + return Subscript(primary, assigning, subscripts,
6.1318 + lineno=extractLineNo(nodelist))
6.1319 +
6.1320 + def com_subscript(self, node):
6.1321 + # slice_item: expression | proper_slice | ellipsis
6.1322 + ch = node[1]
6.1323 + t = ch[0]
6.1324 + if t == token.DOT and node[2][0] == token.DOT:
6.1325 + return Ellipsis()
6.1326 + if t == token.COLON or len(node) > 2:
6.1327 + return self.com_sliceobj(node)
6.1328 + return self.com_node(ch)
6.1329 +
6.1330 + def com_sliceobj(self, node):
6.1331 + # proper_slice: short_slice | long_slice
6.1332 + # short_slice: [lower_bound] ":" [upper_bound]
6.1333 + # long_slice: short_slice ":" [stride]
6.1334 + # lower_bound: expression
6.1335 + # upper_bound: expression
6.1336 + # stride: expression
6.1337 + #
6.1338 + # Note: a stride may be further slicing...
6.1339 +
6.1340 + items = []
6.1341 +
6.1342 + if node[1][0] == token.COLON:
6.1343 + items.append(Const(None))
6.1344 + i = 2
6.1345 + else:
6.1346 + items.append(self.com_node(node[1]))
6.1347 + # i == 2 is a COLON
6.1348 + i = 3
6.1349 +
6.1350 + if i < len(node) and node[i][0] == symbol.test:
6.1351 + items.append(self.com_node(node[i]))
6.1352 + i = i + 1
6.1353 + else:
6.1354 + items.append(Const(None))
6.1355 +
6.1356 + # a short_slice has been built. look for long_slice now by looking
6.1357 + # for strides...
6.1358 + for j in range(i, len(node)):
6.1359 + ch = node[j]
6.1360 + if len(ch) == 2:
6.1361 + items.append(Const(None))
6.1362 + else:
6.1363 + items.append(self.com_node(ch[2]))
6.1364 + return Sliceobj(items, lineno=extractLineNo(node))
6.1365 +
6.1366 + def com_slice(self, primary, node, assigning):
6.1367 + # short_slice: [lower_bound] ":" [upper_bound]
6.1368 + lower = upper = None
6.1369 + if len(node) == 3:
6.1370 + if node[1][0] == token.COLON:
6.1371 + upper = self.com_node(node[2])
6.1372 + else:
6.1373 + lower = self.com_node(node[1])
6.1374 + elif len(node) == 4:
6.1375 + lower = self.com_node(node[1])
6.1376 + upper = self.com_node(node[3])
6.1377 + return Slice(primary, assigning, lower, upper,
6.1378 + lineno=extractLineNo(node))
6.1379 +
6.1380 + def get_docstring(self, node, n=None):
6.1381 + if n is None:
6.1382 + n = node[0]
6.1383 + node = node[1:]
6.1384 + if n == symbol.suite:
6.1385 + if len(node) == 1:
6.1386 + return self.get_docstring(node[0])
6.1387 + for sub in node:
6.1388 + if sub[0] == symbol.stmt:
6.1389 + return self.get_docstring(sub)
6.1390 + return None
6.1391 + if n == symbol.file_input:
6.1392 + for sub in node:
6.1393 + if sub[0] == symbol.stmt:
6.1394 + return self.get_docstring(sub)
6.1395 + return None
6.1396 + if n == symbol.atom:
6.1397 + if node[0][0] == token.STRING:
6.1398 + s = ''
6.1399 + for t in node:
6.1400 + s = s + eval(t[1])
6.1401 + return s
6.1402 + return None
6.1403 + if n == symbol.stmt or n == symbol.simple_stmt \
6.1404 + or n == symbol.small_stmt:
6.1405 + return self.get_docstring(node[0])
6.1406 + if n in _doc_nodes and len(node) == 1:
6.1407 + return self.get_docstring(node[0])
6.1408 + return None
6.1409 +
6.1410 +
6.1411 +_doc_nodes = [
6.1412 + symbol.expr_stmt,
6.1413 + symbol.testlist,
6.1414 + symbol.testlist_safe,
6.1415 + symbol.test,
6.1416 + symbol.or_test,
6.1417 + symbol.and_test,
6.1418 + symbol.not_test,
6.1419 + symbol.comparison,
6.1420 + symbol.expr,
6.1421 + symbol.xor_expr,
6.1422 + symbol.and_expr,
6.1423 + symbol.shift_expr,
6.1424 + symbol.arith_expr,
6.1425 + symbol.term,
6.1426 + symbol.factor,
6.1427 + symbol.power,
6.1428 + ]
6.1429 +
6.1430 +# comp_op: '<' | '>' | '=' | '>=' | '<=' | '<>' | '!=' | '=='
6.1431 +# | 'in' | 'not' 'in' | 'is' | 'is' 'not'
6.1432 +_cmp_types = {
6.1433 + token.LESS : '<',
6.1434 + token.GREATER : '>',
6.1435 + token.EQEQUAL : '==',
6.1436 + token.EQUAL : '==',
6.1437 + token.LESSEQUAL : '<=',
6.1438 + token.GREATEREQUAL : '>=',
6.1439 + token.NOTEQUAL : '!=',
6.1440 + }
6.1441 +
6.1442 +_legal_node_types = [
6.1443 + symbol.funcdef,
6.1444 + symbol.classdef,
6.1445 + symbol.stmt,
6.1446 + symbol.small_stmt,
6.1447 + symbol.flow_stmt,
6.1448 + symbol.simple_stmt,
6.1449 + symbol.compound_stmt,
6.1450 + symbol.expr_stmt,
6.1451 + symbol.print_stmt,
6.1452 + symbol.del_stmt,
6.1453 + symbol.pass_stmt,
6.1454 + symbol.break_stmt,
6.1455 + symbol.continue_stmt,
6.1456 + symbol.return_stmt,
6.1457 + symbol.raise_stmt,
6.1458 + symbol.import_stmt,
6.1459 + symbol.global_stmt,
6.1460 + symbol.exec_stmt,
6.1461 + symbol.assert_stmt,
6.1462 + symbol.if_stmt,
6.1463 + symbol.while_stmt,
6.1464 + symbol.for_stmt,
6.1465 + symbol.try_stmt,
6.1466 + symbol.with_stmt,
6.1467 + symbol.suite,
6.1468 + symbol.testlist,
6.1469 + symbol.testlist_safe,
6.1470 + symbol.test,
6.1471 + symbol.and_test,
6.1472 + symbol.not_test,
6.1473 + symbol.comparison,
6.1474 + symbol.exprlist,
6.1475 + symbol.expr,
6.1476 + symbol.xor_expr,
6.1477 + symbol.and_expr,
6.1478 + symbol.shift_expr,
6.1479 + symbol.arith_expr,
6.1480 + symbol.term,
6.1481 + symbol.factor,
6.1482 + symbol.power,
6.1483 + symbol.atom,
6.1484 + symbol.yield_stmt,
6.1485 + symbol.yield_expr,
6.1486 + ]
6.1487 +
6.1488 +_assign_types = [
6.1489 + symbol.test,
6.1490 + symbol.or_test,
6.1491 + symbol.and_test,
6.1492 + symbol.not_test,
6.1493 + symbol.comparison,
6.1494 + symbol.expr,
6.1495 + symbol.xor_expr,
6.1496 + symbol.and_expr,
6.1497 + symbol.shift_expr,
6.1498 + symbol.arith_expr,
6.1499 + symbol.term,
6.1500 + symbol.factor,
6.1501 + ]
6.1502 +
6.1503 +_names = {}
6.1504 +for k, v in symbol.sym_name.items():
6.1505 + _names[k] = v
6.1506 +for k, v in token.tok_name.items():
6.1507 + _names[k] = v
6.1508 +
6.1509 +def debug_tree(tree):
6.1510 + l = []
6.1511 + for elt in tree:
6.1512 + if isinstance(elt, int):
6.1513 + l.append(_names.get(elt, elt))
6.1514 + elif isinstance(elt, str):
6.1515 + l.append(elt)
6.1516 + else:
6.1517 + l.append(debug_tree(elt))
6.1518 + return l
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/encoders.py Tue Aug 30 16:51:10 2016 +0200
7.3 @@ -0,0 +1,128 @@
7.4 +#!/usr/bin/env python
7.5 +
7.6 +"""
7.7 +Encoder functions, producing representations of program objects.
7.8 +
7.9 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
7.10 +
7.11 +This program is free software; you can redistribute it and/or modify it under
7.12 +the terms of the GNU General Public License as published by the Free Software
7.13 +Foundation; either version 3 of the License, or (at your option) any later
7.14 +version.
7.15 +
7.16 +This program is distributed in the hope that it will be useful, but WITHOUT
7.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
7.19 +details.
7.20 +
7.21 +You should have received a copy of the GNU General Public License along with
7.22 +this program. If not, see <http://www.gnu.org/licenses/>.
7.23 +"""
7.24 +
7.25 +# Output encoding and decoding for the summary files.
7.26 +
7.27 +def encode_attrnames(attrnames):
7.28 +
7.29 + "Encode the 'attrnames' representing usage."
7.30 +
7.31 + return ", ".join(attrnames) or "{}"
7.32 +
7.33 +def encode_constrained(constrained):
7.34 +
7.35 + "Encode the 'constrained' status for program summaries."
7.36 +
7.37 + return constrained and "constrained" or "deduced"
7.38 +
7.39 +def encode_usage(usage):
7.40 +
7.41 + "Encode attribute details from 'usage'."
7.42 +
7.43 + all_attrnames = []
7.44 + for t in usage:
7.45 + all_attrnames.append(t)
7.46 + return ", ".join(all_attrnames) or "{}"
7.47 +
7.48 +def encode_access_location(t):
7.49 +
7.50 + "Encode the access location 't'."
7.51 +
7.52 + path, name, attrname, version = t
7.53 + return "%s %s %s:%d" % (path, name or "{}", attrname, version)
7.54 +
7.55 +def encode_location(t):
7.56 +
7.57 + "Encode the general location 't' in a concise form."
7.58 +
7.59 + path, name, attrname, version = t
7.60 + if name is not None and version is not None:
7.61 + return "%s %s:%d" % (path, name, version)
7.62 + elif name is not None:
7.63 + return "%s %s" % (path, name)
7.64 + else:
7.65 + return "%s :%s" % (path, attrname)
7.66 +
7.67 +def encode_modifiers(modifiers):
7.68 +
7.69 + "Encode assignment details from 'modifiers'."
7.70 +
7.71 + all_modifiers = []
7.72 + for t in modifiers:
7.73 + all_modifiers.append(encode_modifier_term(t))
7.74 + return "".join(all_modifiers)
7.75 +
7.76 +def encode_modifier_term(t):
7.77 +
7.78 + "Encode modifier 't' representing assignment status."
7.79 +
7.80 + assignment = t
7.81 + return assignment and "A" or "_"
7.82 +
7.83 +def decode_modifier_term(s):
7.84 +
7.85 + "Decode modifier term 's' representing assignment status."
7.86 +
7.87 + return s == "A"
7.88 +
7.89 +# Output program encoding.
7.90 +
7.91 +def encode_function_pointer(path):
7.92 +
7.93 + "Encode 'path' as a reference to an output program function."
7.94 +
7.95 + return "__fn_%s" % encode_path(path)
7.96 +
7.97 +def encode_instantiator_pointer(path):
7.98 +
7.99 + "Encode 'path' as a reference to an output program instantiator."
7.100 +
7.101 + return "__new_%s" % encode_path(path)
7.102 +
7.103 +def encode_path(path):
7.104 +
7.105 + "Encode 'path' as an output program object, translating special symbols."
7.106 +
7.107 + if path in reserved_words:
7.108 + return "__%s" % path
7.109 + else:
7.110 + return path.replace("#", "__").replace("$", "__").replace(".", "_")
7.111 +
7.112 +def encode_symbol(symbol_type, path=None):
7.113 +
7.114 + "Encode a symbol with the given 'symbol_type' and optional 'path'."
7.115 +
7.116 + return "__%s%s" % (symbol_type, path and "_%s" % encode_path(path) or "")
7.117 +
7.118 +# Output language reserved words.
7.119 +
7.120 +reserved_words = [
7.121 + "break", "char", "const", "continue",
7.122 + "default", "double", "else",
7.123 + "float", "for",
7.124 + "if", "int", "long",
7.125 + "NULL",
7.126 + "return", "struct",
7.127 + "typedef",
7.128 + "void", "while",
7.129 + ]
7.130 +
7.131 +# vim: tabstop=4 expandtab shiftwidth=4
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/errors.py Tue Aug 30 16:51:10 2016 +0200
8.3 @@ -0,0 +1,87 @@
8.4 +#!/usr/bin/env python
8.5 +
8.6 +"""
8.7 +Error classes.
8.8 +
8.9 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
8.10 + 2014, 2016 Paul Boddie <paul@boddie.org.uk>
8.11 +
8.12 +This program is free software; you can redistribute it and/or modify it under
8.13 +the terms of the GNU General Public License as published by the Free Software
8.14 +Foundation; either version 3 of the License, or (at your option) any later
8.15 +version.
8.16 +
8.17 +This program is distributed in the hope that it will be useful, but WITHOUT
8.18 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8.19 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
8.20 +details.
8.21 +
8.22 +You should have received a copy of the GNU General Public License along with
8.23 +this program. If not, see <http://www.gnu.org/licenses/>.
8.24 +"""
8.25 +
8.26 +class ProcessingError(Exception):
8.27 +
8.28 + "A processing error."
8.29 +
8.30 + pass
8.31 +
8.32 +class ProgramError(ProcessingError):
8.33 +
8.34 + "A general program processing error."
8.35 +
8.36 + def __init__(self, message):
8.37 + self.message = message
8.38 +
8.39 + def __repr__(self):
8.40 + return "%s(%r)" % (self.__class__.__name__, self.message)
8.41 +
8.42 + def __str__(self):
8.43 + return self.message
8.44 +
8.45 +class NodeProcessingError(ProcessingError):
8.46 +
8.47 + "A processing error associated with a particular program node."
8.48 +
8.49 + def __init__(self, message, unit_name=None, astnode=None):
8.50 + self.message = message
8.51 + self.unit_name = unit_name
8.52 + self.astnode = astnode
8.53 +
8.54 + def get_lineno(self, node):
8.55 +
8.56 + "Search for line number information associated with 'node'."
8.57 +
8.58 + if node is None:
8.59 + return None
8.60 +
8.61 + lineno = node.lineno
8.62 + if lineno is not None:
8.63 + return lineno
8.64 + else:
8.65 + for child in node.getChildNodes():
8.66 + lineno = self.get_lineno(child)
8.67 + if lineno is not None:
8.68 + return lineno
8.69 + return None
8.70 +
8.71 + def __repr__(self):
8.72 + return "%s(%r, %r, %r)" % (self.__class__.__name__, self.message, self.unit_name, self.astnode)
8.73 +
8.74 + def __str__(self):
8.75 + lineno = self.get_lineno(self.astnode)
8.76 + return "Error in %s%s: %s" % (self.unit_name, lineno and (" at line %s" % lineno) or "", self.message)
8.77 +
8.78 +class InspectError(NodeProcessingError):
8.79 +
8.80 + "An error during the module inspection process."
8.81 +
8.82 + pass
8.83 +
8.84 +class TranslateError(NodeProcessingError):
8.85 +
8.86 + "An error during the module translation process."
8.87 +
8.88 + pass
8.89 +
8.90 +# vim: tabstop=4 expandtab shiftwidth=4
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/importer.py Tue Aug 30 16:51:10 2016 +0200
9.3 @@ -0,0 +1,710 @@
9.4 +#!/usr/bin/env python
9.5 +
9.6 +"""
9.7 +Import logic.
9.8 +
9.9 +Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
9.10 + 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
9.11 +
9.12 +This program is free software; you can redistribute it and/or modify it under
9.13 +the terms of the GNU General Public License as published by the Free Software
9.14 +Foundation; either version 3 of the License, or (at your option) any later
9.15 +version.
9.16 +
9.17 +This program is distributed in the hope that it will be useful, but WITHOUT
9.18 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9.19 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
9.20 +details.
9.21 +
9.22 +You should have received a copy of the GNU General Public License along with
9.23 +this program. If not, see <http://www.gnu.org/licenses/>.
9.24 +"""
9.25 +
9.26 +from errors import ProgramError
9.27 +from os.path import exists, extsep, getmtime, join
9.28 +from os import listdir, makedirs, remove
9.29 +from common import init_item, readfile, writefile
9.30 +from referencing import Reference
9.31 +import inspector
9.32 +import sys
9.33 +
9.34 +class Importer:
9.35 +
9.36 + "An import machine, searching for and loading modules."
9.37 +
9.38 + def __init__(self, path, cache=None, verbose=False):
9.39 +
9.40 + """
9.41 + Initialise the importer with the given search 'path' - a list of
9.42 + directories to search for Python modules.
9.43 +
9.44 + The optional 'cache' should be the name of a directory used to store
9.45 + cached module information.
9.46 +
9.47 + The optional 'verbose' parameter causes output concerning the activities
9.48 + of the object to be produced if set to a true value (not the default).
9.49 + """
9.50 +
9.51 + self.path = path
9.52 + self.cache = cache
9.53 + self.verbose = verbose
9.54 +
9.55 + self.modules = {}
9.56 + self.modules_ordered = []
9.57 + self.loading = set()
9.58 + self.hidden = {}
9.59 + self.revealing = {}
9.60 + self.invalidated = set()
9.61 +
9.62 + self.objects = {}
9.63 + self.classes = {}
9.64 + self.function_parameters = {}
9.65 + self.function_defaults = {}
9.66 + self.function_targets = {}
9.67 + self.function_arguments = {}
9.68 +
9.69 + # Derived information.
9.70 +
9.71 + self.subclasses = {}
9.72 +
9.73 + # Attributes of different object types.
9.74 +
9.75 + self.all_class_attrs = {}
9.76 + self.all_instance_attrs = {}
9.77 + self.all_instance_attr_constants = {}
9.78 + self.all_combined_attrs = {}
9.79 + self.all_module_attrs = {}
9.80 + self.all_shadowed_attrs = {}
9.81 +
9.82 + # References to external names and aliases within program units.
9.83 +
9.84 + self.all_name_references = {}
9.85 + self.all_initialised_names = {}
9.86 + self.all_aliased_names = {}
9.87 +
9.88 + # General attribute accesses.
9.89 +
9.90 + self.all_attr_accesses = {}
9.91 + self.all_const_accesses = {}
9.92 + self.all_attr_access_modifiers = {}
9.93 +
9.94 + # Constant literals and values.
9.95 +
9.96 + self.all_constants = {}
9.97 + self.all_constant_values = {}
9.98 +
9.99 + self.make_cache()
9.100 +
9.101 + def make_cache(self):
9.102 + if self.cache and not exists(self.cache):
9.103 + makedirs(self.cache)
9.104 +
9.105 + def check_cache(self, details):
9.106 +
9.107 + """
9.108 + Check whether the cache applies for the given 'details', invalidating it
9.109 + if it does not.
9.110 + """
9.111 +
9.112 + recorded_details = self.get_cache_details()
9.113 +
9.114 + if recorded_details != details:
9.115 + self.remove_cache()
9.116 +
9.117 + writefile(self.get_cache_details_filename(), details)
9.118 +
9.119 + def get_cache_details_filename(self):
9.120 +
9.121 + "Return the filename for the cache details."
9.122 +
9.123 + return join(self.cache, "$details")
9.124 +
9.125 + def get_cache_details(self):
9.126 +
9.127 + "Return details of the cache."
9.128 +
9.129 + details_filename = self.get_cache_details_filename()
9.130 +
9.131 + if not exists(details_filename):
9.132 + return None
9.133 + else:
9.134 + return readfile(details_filename)
9.135 +
9.136 + def remove_cache(self):
9.137 +
9.138 + "Remove the contents of the cache."
9.139 +
9.140 + for filename in listdir(self.cache):
9.141 + remove(join(self.cache, filename))
9.142 +
9.143 + def to_cache(self):
9.144 +
9.145 + "Write modules to the cache."
9.146 +
9.147 + if self.cache:
9.148 + for module_name, module in self.modules.items():
9.149 + module.to_cache(join(self.cache, module_name))
9.150 +
9.151 + # Object retrieval and storage.
9.152 +
9.153 + def get_object(self, name):
9.154 +
9.155 + """
9.156 + Return a reference for the given 'name' or None if no such object
9.157 + exists.
9.158 + """
9.159 +
9.160 + return self.objects.get(name)
9.161 +
9.162 + def set_object(self, name, value=None):
9.163 +
9.164 + "Set the object with the given 'name' and the given 'value'."
9.165 +
9.166 + if isinstance(value, Reference):
9.167 + ref = value.alias(name)
9.168 + else:
9.169 + ref = Reference(value, name)
9.170 +
9.171 + self.objects[name] = ref
9.172 +
9.173 + # Indirect object retrieval.
9.174 +
9.175 + def get_attributes(self, ref, attrname):
9.176 +
9.177 + """
9.178 + Return attributes provided by 'ref' for 'attrname'. Class attributes
9.179 + may be provided by instances.
9.180 + """
9.181 +
9.182 + kind = ref.get_kind()
9.183 + if kind == "<class>":
9.184 + ref = self.get_class_attribute(ref.get_origin(), attrname)
9.185 + return ref and set([ref]) or set()
9.186 + elif kind == "<instance>":
9.187 + return self.get_combined_attributes(ref.get_origin(), attrname)
9.188 + elif kind == "<module>":
9.189 + ref = self.get_module_attribute(ref.get_origin(), attrname)
9.190 + return ref and set([ref]) or set()
9.191 + else:
9.192 + return set()
9.193 +
9.194 + def get_class_attribute(self, object_type, attrname):
9.195 +
9.196 + "Return from 'object_type' the details of class attribute 'attrname'."
9.197 +
9.198 + attr = self.all_class_attrs[object_type].get(attrname)
9.199 + return attr and self.get_object(attr)
9.200 +
9.201 + def get_instance_attributes(self, object_type, attrname):
9.202 +
9.203 + """
9.204 + Return from 'object_type' the details of instance attribute 'attrname'.
9.205 + """
9.206 +
9.207 + consts = self.all_instance_attr_constants.get(object_type)
9.208 + attrs = set()
9.209 + for attr in self.all_instance_attrs[object_type].get(attrname, []):
9.210 + attrs.add(consts and consts.get(attrname) or Reference("<var>", attr))
9.211 + return attrs
9.212 +
9.213 + def get_combined_attributes(self, object_type, attrname):
9.214 +
9.215 + """
9.216 + Return from 'object_type' the details of class or instance attribute
9.217 + 'attrname'.
9.218 + """
9.219 +
9.220 + ref = self.get_class_attribute(object_type, attrname)
9.221 + refs = ref and set([ref]) or set()
9.222 + refs.update(self.get_instance_attributes(object_type, attrname))
9.223 + return refs
9.224 +
9.225 + def get_module_attribute(self, object_type, attrname):
9.226 +
9.227 + "Return from 'object_type' the details of module attribute 'attrname'."
9.228 +
9.229 + if attrname in self.all_module_attrs[object_type]:
9.230 + return self.get_object("%s.%s" % (object_type, attrname))
9.231 + else:
9.232 + return None
9.233 +
9.234 + # Module management.
9.235 +
9.236 + def get_modules(self):
9.237 +
9.238 + "Return all modules known to the importer."
9.239 +
9.240 + return self.modules.values()
9.241 +
9.242 + def get_module(self, name, hidden=False):
9.243 +
9.244 + "Return the module with the given 'name'."
9.245 +
9.246 + if not self.modules.has_key(name):
9.247 + return None
9.248 +
9.249 + # Obtain the module and attempt to reveal it.
9.250 +
9.251 + module = self.modules[name]
9.252 + if not hidden:
9.253 + self.reveal_module(module)
9.254 + return module
9.255 +
9.256 + def reveal_module(self, module):
9.257 +
9.258 + "Check if 'module' is hidden and reveal it."
9.259 +
9.260 + if module.name in self.hidden:
9.261 + del self.hidden[module.name]
9.262 +
9.263 + # Reveal referenced modules.
9.264 +
9.265 + module.reveal_referenced()
9.266 +
9.267 + def set_revealing(self, module, name, instigator):
9.268 +
9.269 + """
9.270 + Make the revealing of 'module' conditional on 'name' for the given
9.271 + 'instigator' of the reveal operation.
9.272 + """
9.273 +
9.274 + self.revealing[module.name].add((name, instigator))
9.275 +
9.276 + # Program operations.
9.277 +
9.278 + def initialise(self, filename, reset=False):
9.279 +
9.280 + """
9.281 + Initialise a program whose main module is 'filename', resetting the
9.282 + cache if 'reset' is true. Return the main module.
9.283 + """
9.284 +
9.285 + if reset:
9.286 + self.remove_cache()
9.287 + self.check_cache(filename)
9.288 +
9.289 + # Load the program itself.
9.290 +
9.291 + m = self.load_from_file(filename)
9.292 +
9.293 + # Resolve dependencies within the program.
9.294 +
9.295 + for module in self.modules_ordered:
9.296 + module.resolve()
9.297 +
9.298 + return m
9.299 +
9.300 + def finalise(self):
9.301 +
9.302 + "Finalise the inspected program."
9.303 +
9.304 + self.finalise_classes()
9.305 + self.remove_hidden()
9.306 + self.to_cache()
9.307 + self.set_class_types()
9.308 + self.define_instantiators()
9.309 + self.collect_constants()
9.310 +
9.311 + def finalise_classes(self):
9.312 +
9.313 + "Finalise the class relationships and attributes."
9.314 +
9.315 + self.derive_inherited_attrs()
9.316 + self.derive_subclasses()
9.317 + self.derive_shadowed_attrs()
9.318 +
9.319 + def derive_inherited_attrs(self):
9.320 +
9.321 + "Derive inherited attributes for classes throughout the program."
9.322 +
9.323 + for name in self.classes.keys():
9.324 + self.propagate_attrs_for_class(name)
9.325 +
9.326 + def propagate_attrs_for_class(self, name, visited=None):
9.327 +
9.328 + "Propagate inherited attributes for class 'name'."
9.329 +
9.330 + # Visit classes only once.
9.331 +
9.332 + if self.all_combined_attrs.has_key(name):
9.333 + return
9.334 +
9.335 + visited = visited or []
9.336 +
9.337 + if name in visited:
9.338 + raise ProgramError, "Class %s may not inherit from itself: %s -> %s." % (name, " -> ".join(visited), name)
9.339 +
9.340 + visited.append(name)
9.341 +
9.342 + class_attrs = {}
9.343 + instance_attrs = {}
9.344 +
9.345 + # Aggregate the attributes from base classes, recording the origins of
9.346 + # applicable attributes.
9.347 +
9.348 + for base in self.classes[name][::-1]:
9.349 +
9.350 + # Get the identity of the class from the reference.
9.351 +
9.352 + base = base.get_origin()
9.353 +
9.354 + # Define the base class completely before continuing with this
9.355 + # class.
9.356 +
9.357 + self.propagate_attrs_for_class(base, visited)
9.358 + class_attrs.update(self.all_class_attrs[base])
9.359 +
9.360 + # Instance attribute origins are combined if different.
9.361 +
9.362 + for key, values in self.all_instance_attrs[base].items():
9.363 + init_item(instance_attrs, key, set)
9.364 + instance_attrs[key].update(values)
9.365 +
9.366 + # Class attributes override those defined earlier in the hierarchy.
9.367 +
9.368 + class_attrs.update(self.all_class_attrs.get(name, {}))
9.369 +
9.370 + # Instance attributes are merely added if not already defined.
9.371 +
9.372 + for key in self.all_instance_attrs.get(name, []):
9.373 + if not instance_attrs.has_key(key):
9.374 + instance_attrs[key] = set(["%s.%s" % (name, key)])
9.375 +
9.376 + self.all_class_attrs[name] = class_attrs
9.377 + self.all_instance_attrs[name] = instance_attrs
9.378 + self.all_combined_attrs[name] = set(class_attrs.keys()).union(instance_attrs.keys())
9.379 +
9.380 + def derive_subclasses(self):
9.381 +
9.382 + "Derive subclass details for classes."
9.383 +
9.384 + for name, bases in self.classes.items():
9.385 + for base in bases:
9.386 +
9.387 + # Get the identity of the class from the reference.
9.388 +
9.389 + base = base.get_origin()
9.390 + self.subclasses[base].add(name)
9.391 +
9.392 + def derive_shadowed_attrs(self):
9.393 +
9.394 + "Derive shadowed attributes for classes."
9.395 +
9.396 + for name, attrs in self.all_instance_attrs.items():
9.397 + attrs = set(attrs.keys()).intersection(self.all_class_attrs[name].keys())
9.398 + if attrs:
9.399 + self.all_shadowed_attrs[name] = attrs
9.400 +
9.401 + def remove_hidden(self):
9.402 +
9.403 + "Remove all hidden modules."
9.404 +
9.405 + # First reveal any modules exposing names.
9.406 +
9.407 + for modname, names in self.revealing.items():
9.408 + module = self.modules[modname]
9.409 +
9.410 + # Obtain the imported names and determine whether they should cause
9.411 + # the module to be revealed.
9.412 +
9.413 + for (name, instigator) in names:
9.414 + if module is not instigator:
9.415 +
9.416 + # Only if an object is provided by the module should the
9.417 + # module be revealed. References to objects in other modules
9.418 + # should not in themselves expose the module in which those
9.419 + # references occur.
9.420 +
9.421 + ref = module.get_global(name)
9.422 + if ref and ref.provided_by_module(module.name):
9.423 + self.reveal_module(module)
9.424 + instigator.revealed.add(module)
9.425 +
9.426 + # Then remove all modules that are still hidden.
9.427 +
9.428 + for modname in self.hidden:
9.429 + module = self.modules[modname]
9.430 + module.unpropagate()
9.431 + del self.modules[modname]
9.432 + ref = self.objects.get(modname)
9.433 + if ref and ref.get_kind() == "<module>":
9.434 + del self.objects[modname]
9.435 +
9.436 + def set_class_types(self):
9.437 +
9.438 + "Set the type of each class."
9.439 +
9.440 + ref = self.get_object("__builtins__.type")
9.441 + for attrs in self.all_class_attrs.values():
9.442 + attrs["__class__"] = ref.get_origin()
9.443 +
9.444 + def define_instantiators(self):
9.445 +
9.446 + """
9.447 + Consolidate parameter and default details, incorporating initialiser
9.448 + details to define instantiator signatures.
9.449 + """
9.450 +
9.451 + for cls, attrs in self.all_class_attrs.items():
9.452 + initialiser = attrs["__init__"]
9.453 + self.function_parameters[cls] = self.function_parameters[initialiser][1:]
9.454 + self.function_defaults[cls] = self.function_defaults[initialiser]
9.455 +
9.456 + def collect_constants(self):
9.457 +
9.458 + "Get constants from all active modules."
9.459 +
9.460 + for module in self.modules.values():
9.461 + self.all_constants.update(module.constants)
9.462 +
9.463 + # Import methods.
9.464 +
9.465 + def find_in_path(self, name):
9.466 +
9.467 + """
9.468 + Find the given module 'name' in the search path, returning None where no
9.469 + such module could be found, or a 2-tuple from the 'find' method
9.470 + otherwise.
9.471 + """
9.472 +
9.473 + for d in self.path:
9.474 + m = self.find(d, name)
9.475 + if m: return m
9.476 + return None
9.477 +
9.478 + def find(self, d, name):
9.479 +
9.480 + """
9.481 + In the directory 'd', find the given module 'name', where 'name' can
9.482 + either refer to a single file module or to a package. Return None if the
9.483 + 'name' cannot be associated with either a file or a package directory,
9.484 + or a 2-tuple from '_find_package' or '_find_module' otherwise.
9.485 + """
9.486 +
9.487 + m = self._find_package(d, name)
9.488 + if m: return m
9.489 + m = self._find_module(d, name)
9.490 + if m: return m
9.491 + return None
9.492 +
9.493 + def _find_module(self, d, name):
9.494 +
9.495 + """
9.496 + In the directory 'd', find the given module 'name', returning None where
9.497 + no suitable file exists in the directory, or a 2-tuple consisting of
9.498 + None (indicating that no package directory is involved) and a filename
9.499 + indicating the location of the module.
9.500 + """
9.501 +
9.502 + name_py = name + extsep + "py"
9.503 + filename = self._find_file(d, name_py)
9.504 + if filename:
9.505 + return None, filename
9.506 + return None
9.507 +
9.508 + def _find_package(self, d, name):
9.509 +
9.510 + """
9.511 + In the directory 'd', find the given package 'name', returning None
9.512 + where no suitable package directory exists, or a 2-tuple consisting of
9.513 + a directory (indicating the location of the package directory itself)
9.514 + and a filename indicating the location of the __init__.py module which
9.515 + declares the package's top-level contents.
9.516 + """
9.517 +
9.518 + filename = self._find_file(d, name)
9.519 + if filename:
9.520 + init_py = "__init__" + extsep + "py"
9.521 + init_py_filename = self._find_file(filename, init_py)
9.522 + if init_py_filename:
9.523 + return filename, init_py_filename
9.524 + return None
9.525 +
9.526 + def _find_file(self, d, filename):
9.527 +
9.528 + """
9.529 + Return the filename obtained when searching the directory 'd' for the
9.530 + given 'filename', or None if no actual file exists for the filename.
9.531 + """
9.532 +
9.533 + filename = join(d, filename)
9.534 + if exists(filename):
9.535 + return filename
9.536 + else:
9.537 + return None
9.538 +
9.539 + def load(self, name, return_leaf=False, hidden=False):
9.540 +
9.541 + """
9.542 + Load the module or package with the given 'name'. Return an object
9.543 + referencing the loaded module or package, or None if no such module or
9.544 + package exists.
9.545 +
9.546 + Where 'return_leaf' is specified, the final module in the chain is
9.547 + returned. Where 'hidden' is specified, the module is marked as hidden.
9.548 + """
9.549 +
9.550 + if return_leaf:
9.551 + name_for_return = name
9.552 + else:
9.553 + name_for_return = name.split(".")[0]
9.554 +
9.555 + # Loaded modules are returned immediately.
9.556 + # Modules may be known but not yet loading (having been registered as
9.557 + # submodules), loading, loaded, or completely unknown.
9.558 +
9.559 + module = self.get_module(name, hidden)
9.560 +
9.561 + if module:
9.562 + return self.modules[name_for_return]
9.563 +
9.564 + # Otherwise, modules are loaded.
9.565 +
9.566 + if self.verbose:
9.567 + print >>sys.stderr, "Loading", name
9.568 +
9.569 + # Split the name into path components, and try to find the uppermost in
9.570 + # the search path.
9.571 +
9.572 + path = name.split(".")
9.573 + path_so_far = []
9.574 + top = module = None
9.575 +
9.576 + for p in path:
9.577 +
9.578 + # Get the module's filesystem details.
9.579 +
9.580 + if not path_so_far:
9.581 + m = self.find_in_path(p)
9.582 + elif d:
9.583 + m = self.find(d, p)
9.584 + else:
9.585 + m = None
9.586 +
9.587 + path_so_far.append(p)
9.588 + module_name = ".".join(path_so_far)
9.589 +
9.590 + if not m:
9.591 + if self.verbose:
9.592 + print >>sys.stderr, "Not found (%s)" % name
9.593 +
9.594 + return None # NOTE: Import error.
9.595 +
9.596 + # Get the module itself.
9.597 +
9.598 + d, filename = m
9.599 + submodule = self.load_from_file(filename, module_name, hidden)
9.600 +
9.601 + if module is None:
9.602 + top = submodule
9.603 +
9.604 + module = submodule
9.605 +
9.606 + # Return either the deepest or the uppermost module.
9.607 +
9.608 + return return_leaf and module or top
9.609 +
9.610 + def load_from_file(self, filename, module_name=None, hidden=False):
9.611 +
9.612 + "Load the module from the given 'filename'."
9.613 +
9.614 + if module_name is None:
9.615 + module_name = "__main__"
9.616 +
9.617 + module = self.modules.get(module_name)
9.618 +
9.619 + if not module:
9.620 +
9.621 + # Try to load from cache.
9.622 +
9.623 + module = self.load_from_cache(filename, module_name, hidden)
9.624 + if module:
9.625 + return module
9.626 +
9.627 + # If no cache entry exists, load from file.
9.628 +
9.629 + module = inspector.InspectedModule(module_name, self)
9.630 + self.add_module(module_name, module)
9.631 + self.update_cache_validity(module)
9.632 +
9.633 + # Initiate loading if not already in progress.
9.634 +
9.635 + if not module.loaded and module not in self.loading:
9.636 + self._load(module, module_name, hidden, lambda m: m.parse, filename)
9.637 +
9.638 + return module
9.639 +
9.640 + def update_cache_validity(self, module):
9.641 +
9.642 + "Make 'module' valid in the cache, but invalidate accessing modules."
9.643 +
9.644 + self.invalidated.update(module.accessing_modules)
9.645 + if module.name in self.invalidated:
9.646 + self.invalidated.remove(module.name)
9.647 +
9.648 + def source_is_new(self, filename, module_name):
9.649 +
9.650 + "Return whether 'filename' is newer than the cached 'module_name'."
9.651 +
9.652 + if self.cache:
9.653 + cache_filename = join(self.cache, module_name)
9.654 + return not exists(cache_filename) or \
9.655 + getmtime(filename) > getmtime(cache_filename) or \
9.656 + module_name in self.invalidated
9.657 + else:
9.658 + return True
9.659 +
9.660 + def load_from_cache(self, filename, module_name, hidden=False):
9.661 +
9.662 + "Return a module residing in the cache."
9.663 +
9.664 + module = self.modules.get(module_name)
9.665 +
9.666 + if not self.source_is_new(filename, module_name):
9.667 +
9.668 + if not module:
9.669 + module = inspector.CachedModule(module_name, self)
9.670 + self.add_module(module_name, module)
9.671 +
9.672 + if not module.loaded and module not in self.loading:
9.673 + filename = join(self.cache, module_name)
9.674 + self._load(module, module_name, hidden, lambda m: m.from_cache, filename)
9.675 +
9.676 + return module
9.677 +
9.678 + def _load(self, module, module_name, hidden, fn, filename):
9.679 +
9.680 + """
9.681 + Load 'module' for the given 'module_name', with the module being hidden
9.682 + if 'hidden' is a true value, and with 'fn' performing an invocation on
9.683 + the module with the given 'filename'.
9.684 + """
9.685 +
9.686 + # Indicate that the module is hidden if requested.
9.687 +
9.688 + if hidden:
9.689 + self.hidden[module_name] = module
9.690 +
9.691 + # Indicate that loading is in progress and load the module.
9.692 +
9.693 + self.loading.add(module)
9.694 + if self.verbose:
9.695 + print >>sys.stderr, "Loading", filename
9.696 + fn(module)(filename)
9.697 + if self.verbose:
9.698 + print >>sys.stderr, "Loaded", filename
9.699 + self.loading.remove(module)
9.700 +
9.701 + self.modules_ordered.append(module)
9.702 +
9.703 + def add_module(self, module_name, module):
9.704 +
9.705 + """
9.706 + Return the module with the given 'module_name', adding a new module
9.707 + object if one does not already exist.
9.708 + """
9.709 +
9.710 + self.modules[module_name] = module
9.711 + self.objects[module_name] = Reference("<module>", module_name)
9.712 +
9.713 +# vim: tabstop=4 expandtab shiftwidth=4
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/inspector.py Tue Aug 30 16:51:10 2016 +0200
10.3 @@ -0,0 +1,1765 @@
10.4 +#!/usr/bin/env python
10.5 +
10.6 +"""
10.7 +Inspect and obtain module structure.
10.8 +
10.9 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,
10.10 + 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
10.11 +
10.12 +This program is free software; you can redistribute it and/or modify it under
10.13 +the terms of the GNU General Public License as published by the Free Software
10.14 +Foundation; either version 3 of the License, or (at your option) any later
10.15 +version.
10.16 +
10.17 +This program is distributed in the hope that it will be useful, but WITHOUT
10.18 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10.19 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10.20 +details.
10.21 +
10.22 +You should have received a copy of the GNU General Public License along with
10.23 +this program. If not, see <http://www.gnu.org/licenses/>.
10.24 +"""
10.25 +
10.26 +from branching import BranchTracker
10.27 +from common import *
10.28 +from modules import *
10.29 +from os import listdir
10.30 +from os.path import extsep, split, splitext
10.31 +from referencing import Reference
10.32 +import compiler
10.33 +import sys
10.34 +
10.35 +class AccessRef(Result):
10.36 +
10.37 + """
10.38 + A reference to an attribute access that is generally only returned from a
10.39 + processed access for possible initialiser resolution for assignments.
10.40 + """
10.41 +
10.42 + def __init__(self, original_name, attrnames, number):
10.43 + self.original_name = original_name
10.44 + self.attrnames = attrnames
10.45 + self.number = number
10.46 +
10.47 + def reference(self):
10.48 + return None
10.49 +
10.50 + def __repr__(self):
10.51 + return "AccessRef(%r, %r, %r)" % (self.original_name, self.attrnames, self.number)
10.52 +
10.53 +class InvocationRef(Result):
10.54 +
10.55 + "An invocation of a name reference."
10.56 +
10.57 + def __init__(self, name_ref):
10.58 + self.name_ref = name_ref
10.59 +
10.60 + def __repr__(self):
10.61 + return "InvocationRef(%r)" % self.name_ref
10.62 +
10.63 +class InspectedModule(BasicModule, CacheWritingModule):
10.64 +
10.65 + "A module inspector."
10.66 +
10.67 + def __init__(self, name, importer):
10.68 + BasicModule.__init__(self, name, importer)
10.69 + self.in_class = False
10.70 + self.in_conditional = False
10.71 + self.global_attr_accesses = {}
10.72 +
10.73 + # Nested scope handling.
10.74 +
10.75 + self.parent_function = None
10.76 + self.propagated_names = {}
10.77 +
10.78 + # Usage tracking.
10.79 +
10.80 + self.trackers = []
10.81 + self.attr_accessor_branches = {}
10.82 +
10.83 + def __repr__(self):
10.84 + return "InspectedModule(%r, %r)" % (self.name, self.importer)
10.85 +
10.86 + def parse(self, filename):
10.87 +
10.88 + "Parse the file having the given 'filename'."
10.89 +
10.90 + self.parse_file(filename)
10.91 +
10.92 + # Inspect the module.
10.93 +
10.94 + self.start_tracking_in_module()
10.95 +
10.96 + # Detect and record imports and globals declared in the module.
10.97 +
10.98 + self.assign_general_local("__name__", self.get_constant("str", self.name))
10.99 + self.assign_general_local("__file__", self.get_constant("str", filename))
10.100 + self.process_structure(self.astnode)
10.101 +
10.102 + # Set the class of the module after the definition has occurred.
10.103 +
10.104 + ref = self.get_builtin("object")
10.105 + self.set_name("__class__", ref)
10.106 +
10.107 + # Get module-level attribute usage details.
10.108 +
10.109 + self.stop_tracking_in_module()
10.110 +
10.111 + # Check names used and resolve them.
10.112 +
10.113 + self.register_submodules()
10.114 + self.loaded = True
10.115 +
10.116 + def register_submodules(self):
10.117 +
10.118 + "For package modules add submodule details."
10.119 +
10.120 + if splitext(split(self.filename)[1])[0] == "__init__":
10.121 + for subname in listdir(split(self.filename)[0]):
10.122 + name, ext = splitext(subname)
10.123 +
10.124 + # Add modules whose names are not already defined as globals.
10.125 +
10.126 + if ext == ("%spy" % extsep) and name != "__init__" and not self.get_global(name):
10.127 + module_name = self.get_global_path(name)
10.128 + top, submodule = self.get_module(module_name, True)
10.129 + self.set_module(name, submodule, hidden=True)
10.130 +
10.131 + def check_special(self):
10.132 +
10.133 + "Check special names."
10.134 +
10.135 + for name, value in self.special.items():
10.136 + if value.has_kind("<depends>"):
10.137 + self.find_imported_name(name, self.name)
10.138 + self.special[name] = self.get_object(value.get_origin())
10.139 +
10.140 + def check_names_used(self):
10.141 +
10.142 + "Check the names used by each function."
10.143 +
10.144 + for path in self.names_used.keys():
10.145 + self.check_names_used_for_path(path)
10.146 +
10.147 + def check_names_used_for_path(self, path):
10.148 +
10.149 + "Check the names used by the given 'path'."
10.150 +
10.151 + names = self.names_used.get(path)
10.152 + if not names:
10.153 + return
10.154 +
10.155 + in_function = self.function_locals.has_key(path)
10.156 +
10.157 + for name in names:
10.158 + if name in predefined_constants or in_function and name in self.function_locals[path]:
10.159 + continue
10.160 +
10.161 + # Resolve names that have been imported locally.
10.162 +
10.163 + self.find_imported_name(name, path)
10.164 +
10.165 + # Find local definitions.
10.166 +
10.167 + key = "%s.%s" % (path, name)
10.168 + ref = self.get_object(key)
10.169 + if ref:
10.170 + self.name_references[key] = ref.final() or key
10.171 + self.resolve_accesses(path, name, ref)
10.172 + continue
10.173 +
10.174 + # Resolve names that have been imported globally.
10.175 +
10.176 + self.find_imported_name(name, self.name)
10.177 +
10.178 + # Find global or built-in definitions.
10.179 +
10.180 + ref = self.get_global_or_builtin(name)
10.181 + objpath = ref and (ref.final() or ref.get_name())
10.182 + if objpath:
10.183 + self.name_references[key] = objpath
10.184 + self.resolve_accesses(path, name, ref)
10.185 + continue
10.186 +
10.187 + print >>sys.stderr, "Name not recognised: %s in %s" % (name, path)
10.188 + init_item(self.names_missing, path, set)
10.189 + self.names_missing[path].add(name)
10.190 +
10.191 + def resolve_members(self):
10.192 +
10.193 + "Resolve any members referring to deferred references."
10.194 +
10.195 + for name, ref in self.objects.items():
10.196 + if ref.has_kind("<depends>"):
10.197 + ref = self.get_object(ref.get_origin())
10.198 + ref = ref.alias(name)
10.199 + self.importer.objects[name] = self.objects[name] = ref
10.200 +
10.201 + def resolve_accesses(self, path, name, ref):
10.202 +
10.203 + """
10.204 + Resolve any unresolved accesses in the function at the given 'path'
10.205 + for the given 'name' corresponding to the indicated 'ref'. Note that
10.206 + this mechanism cannot resolve things like inherited methods because
10.207 + they are not recorded as program objects in their inherited locations.
10.208 + """
10.209 +
10.210 + attr_accesses = self.global_attr_accesses.get(path)
10.211 + all_attrnames = attr_accesses and attr_accesses.get(name)
10.212 +
10.213 + if not all_attrnames:
10.214 + return
10.215 +
10.216 + # Insist on constant accessors.
10.217 +
10.218 + if not ref.has_kind(["<class>", "<module>"]):
10.219 + return
10.220 +
10.221 + found_attrnames = set()
10.222 +
10.223 + for attrnames in all_attrnames:
10.224 +
10.225 + # Start with the resolved name, adding attributes.
10.226 +
10.227 + attrs = ref.get_path()
10.228 + remaining = attrnames.split(".")
10.229 + last_ref = ref
10.230 +
10.231 + # Add each component, testing for a constant object.
10.232 +
10.233 + while remaining:
10.234 + attrname = remaining[0]
10.235 + attrs.append(attrname)
10.236 + del remaining[0]
10.237 +
10.238 + # Find any constant object reference.
10.239 +
10.240 + attr_ref = self.get_object(".".join(attrs))
10.241 +
10.242 + # Non-constant accessors terminate the traversal.
10.243 +
10.244 + if not attr_ref.has_kind(["<class>", "<module>", "<function>"]):
10.245 +
10.246 + # Provide the furthest constant accessor unless the final
10.247 + # access can be resolved.
10.248 +
10.249 + if remaining:
10.250 + remaining.insert(0, attrs.pop())
10.251 + else:
10.252 + last_ref = attr_ref
10.253 + break
10.254 +
10.255 + # A module may expose an attribute imported from a hidden
10.256 + # module.
10.257 +
10.258 + elif last_ref.has_kind("<module>"):
10.259 + module, leaf_module = self.get_module(last_ref.get_origin())
10.260 + self.find_imported_name(attrname, module.name, module)
10.261 +
10.262 + # Follow any reference to a constant object.
10.263 + # Where the given name refers to an object in another location,
10.264 + # switch to the other location in order to be able to test its
10.265 + # attributes.
10.266 +
10.267 + last_ref = attr_ref
10.268 + attrs = attr_ref.get_path()
10.269 +
10.270 + # Record a constant accessor only if an object was found
10.271 + # that is different from the namespace involved.
10.272 +
10.273 + if last_ref:
10.274 + objpath = ".".join(attrs)
10.275 + if objpath != path:
10.276 +
10.277 + # Establish a constant access.
10.278 +
10.279 + init_item(self.const_accesses, path, dict)
10.280 + self.const_accesses[path][(name, attrnames)] = (objpath, last_ref, ".".join(remaining))
10.281 +
10.282 + if len(attrs) > 1:
10.283 + found_attrnames.add(attrs[1])
10.284 +
10.285 + # Remove any usage records for the name.
10.286 +
10.287 + if found_attrnames:
10.288 +
10.289 + # NOTE: Should be only one name version.
10.290 +
10.291 + versions = []
10.292 + for version in self.attr_usage[path][name]:
10.293 + new_usage = set()
10.294 + for usage in version:
10.295 + if found_attrnames.intersection(usage):
10.296 + new_usage.add(tuple(set(usage).difference(found_attrnames)))
10.297 + else:
10.298 + new_usage.add(usage)
10.299 + versions.append(new_usage)
10.300 +
10.301 + self.attr_usage[path][name] = versions
10.302 +
10.303 + def resolve_initialisers(self):
10.304 +
10.305 + "Resolve initialiser values for names."
10.306 +
10.307 + # Get the initialisers in each namespace.
10.308 +
10.309 + for path, name_initialisers in self.name_initialisers.items():
10.310 + const_accesses = self.const_accesses.get(path)
10.311 +
10.312 + # Resolve values for each name in a scope.
10.313 +
10.314 + for name, values in name_initialisers.items():
10.315 + if path == self.name:
10.316 + assigned_path = name
10.317 + else:
10.318 + assigned_path = "%s.%s" % (path, name)
10.319 +
10.320 + initialised_names = {}
10.321 + aliased_names = {}
10.322 +
10.323 + for i, name_ref in enumerate(values):
10.324 +
10.325 + # Unwrap invocations.
10.326 +
10.327 + if isinstance(name_ref, InvocationRef):
10.328 + invocation = True
10.329 + name_ref = name_ref.name_ref
10.330 + else:
10.331 + invocation = False
10.332 +
10.333 + # Obtain a usable reference from names or constants.
10.334 +
10.335 + if isinstance(name_ref, ResolvedNameRef):
10.336 + if not name_ref.reference():
10.337 + continue
10.338 + ref = name_ref.reference()
10.339 +
10.340 + # Obtain a reference from instances.
10.341 +
10.342 + elif isinstance(name_ref, InstanceRef):
10.343 + if not name_ref.reference():
10.344 + continue
10.345 + ref = name_ref.reference()
10.346 +
10.347 + # Resolve accesses that employ constants.
10.348 +
10.349 + elif isinstance(name_ref, AccessRef):
10.350 + ref = None
10.351 +
10.352 + if const_accesses:
10.353 + resolved_access = const_accesses.get((name_ref.original_name, name_ref.attrnames))
10.354 + if resolved_access:
10.355 + objpath, ref, remaining_attrnames = resolved_access
10.356 + if remaining_attrnames:
10.357 + ref = None
10.358 +
10.359 + # Accesses that do not employ constants cannot be resolved,
10.360 + # but they may be resolvable later.
10.361 +
10.362 + if not ref:
10.363 + if not invocation:
10.364 + aliased_names[i] = name_ref.original_name, name_ref.attrnames, name_ref.number
10.365 + continue
10.366 +
10.367 + # Attempt to resolve a plain name reference.
10.368 +
10.369 + elif isinstance(name_ref, LocalNameRef):
10.370 + key = "%s.%s" % (path, name_ref.name)
10.371 + origin = self.name_references.get(key)
10.372 +
10.373 + # Accesses that do not refer to known static objects
10.374 + # cannot be resolved, but they may be resolvable later.
10.375 +
10.376 + if not origin:
10.377 + if not invocation:
10.378 + aliased_names[i] = name_ref.name, None, name_ref.number
10.379 + continue
10.380 +
10.381 + ref = self.get_object(origin)
10.382 + if not ref:
10.383 + continue
10.384 +
10.385 + elif isinstance(name_ref, NameRef):
10.386 + key = "%s.%s" % (path, name_ref.name)
10.387 + origin = self.name_references.get(key)
10.388 + if not origin:
10.389 + continue
10.390 +
10.391 + ref = self.get_object(origin)
10.392 + if not ref:
10.393 + continue
10.394 +
10.395 + else:
10.396 + continue
10.397 +
10.398 + # Convert class invocations to instances.
10.399 +
10.400 + if invocation:
10.401 + ref = ref.has_kind("<class>") and ref.instance_of() or None
10.402 +
10.403 + if ref:
10.404 + initialised_names[i] = ref
10.405 +
10.406 + if initialised_names:
10.407 + self.initialised_names[assigned_path] = initialised_names
10.408 + if aliased_names:
10.409 + self.aliased_names[assigned_path] = aliased_names
10.410 +
10.411 + def resolve_literals(self):
10.412 +
10.413 + "Resolve constant value types."
10.414 +
10.415 + # Get the constants defined in each namespace.
10.416 +
10.417 + for path, constants in self.constants.items():
10.418 + for constant, n in constants.items():
10.419 + objpath = "%s.$c%d" % (path, n)
10.420 + _constant, value_type = self.constant_values[objpath]
10.421 + self.initialised_names[objpath] = {0 : Reference("<instance>", value_type)}
10.422 +
10.423 + # Get the literals defined in each namespace.
10.424 +
10.425 + for path, literals in self.literals.items():
10.426 + for n in range(0, literals):
10.427 + objpath = "%s.$C%d" % (path, n)
10.428 + value_type = self.literal_types[objpath]
10.429 + self.initialised_names[objpath] = {0 : Reference("<instance>", value_type)}
10.430 +
10.431 + def remove_redundant_accessors(self):
10.432 +
10.433 + "Remove now-redundant modifier and accessor information."
10.434 +
10.435 + for path, const_accesses in self.const_accesses.items():
10.436 + accesses = self.attr_accessors.get(path)
10.437 + modifiers = self.attr_access_modifiers.get(path)
10.438 + if not accesses:
10.439 + continue
10.440 + for access in const_accesses.keys():
10.441 + if accesses.has_key(access):
10.442 + del accesses[access]
10.443 + if modifiers and modifiers.has_key(access):
10.444 + del modifiers[access]
10.445 +
10.446 + def set_invocation_usage(self):
10.447 +
10.448 + """
10.449 + Discard the current invocation storage figures, retaining the maximum
10.450 + values.
10.451 + """
10.452 +
10.453 + for path, (current, maximum) in self.function_targets.items():
10.454 + self.importer.function_targets[path] = self.function_targets[path] = maximum
10.455 +
10.456 + for path, (current, maximum) in self.function_arguments.items():
10.457 + self.importer.function_arguments[path] = self.function_arguments[path] = maximum
10.458 +
10.459 + # Module structure traversal.
10.460 +
10.461 + def process_structure_node(self, n):
10.462 +
10.463 + "Process the individual node 'n'."
10.464 +
10.465 + # Module global detection.
10.466 +
10.467 + if isinstance(n, compiler.ast.Global):
10.468 + self.process_global_node(n)
10.469 +
10.470 + # Module import declarations.
10.471 +
10.472 + elif isinstance(n, compiler.ast.From):
10.473 + self.process_from_node(n)
10.474 +
10.475 + elif isinstance(n, compiler.ast.Import):
10.476 + self.process_import_node(n)
10.477 +
10.478 + # Nodes using operator module functions.
10.479 +
10.480 + elif isinstance(n, compiler.ast.Operator):
10.481 + return self.process_operator_node(n)
10.482 +
10.483 + elif isinstance(n, compiler.ast.AugAssign):
10.484 + self.process_augassign_node(n)
10.485 +
10.486 + elif isinstance(n, compiler.ast.Compare):
10.487 + return self.process_compare_node(n)
10.488 +
10.489 + elif isinstance(n, compiler.ast.Slice):
10.490 + return self.process_slice_node(n)
10.491 +
10.492 + elif isinstance(n, compiler.ast.Sliceobj):
10.493 + return self.process_sliceobj_node(n)
10.494 +
10.495 + elif isinstance(n, compiler.ast.Subscript):
10.496 + return self.process_subscript_node(n)
10.497 +
10.498 + # Namespaces within modules.
10.499 +
10.500 + elif isinstance(n, compiler.ast.Class):
10.501 + self.process_class_node(n)
10.502 +
10.503 + elif isinstance(n, compiler.ast.Function):
10.504 + self.process_function_node(n, n.name)
10.505 +
10.506 + elif isinstance(n, compiler.ast.Lambda):
10.507 + return self.process_lambda_node(n)
10.508 +
10.509 + # Assignments.
10.510 +
10.511 + elif isinstance(n, compiler.ast.Assign):
10.512 +
10.513 + # Handle each assignment node.
10.514 +
10.515 + for node in n.nodes:
10.516 + self.process_assignment_node(node, n.expr)
10.517 +
10.518 + # Assignments within non-Assign nodes.
10.519 +
10.520 + elif isinstance(n, compiler.ast.AssName):
10.521 + self.process_assignment_node(n, None)
10.522 +
10.523 + elif isinstance(n, compiler.ast.AssAttr):
10.524 + self.process_attribute_access(n)
10.525 +
10.526 + # Accesses.
10.527 +
10.528 + elif isinstance(n, compiler.ast.Getattr):
10.529 + return self.process_attribute_access(n)
10.530 +
10.531 + # Name recording for later testing.
10.532 +
10.533 + elif isinstance(n, compiler.ast.Name):
10.534 + return self.process_name_node(n)
10.535 +
10.536 + # Conditional statement tracking.
10.537 +
10.538 + elif isinstance(n, compiler.ast.For):
10.539 + self.process_for_node(n)
10.540 +
10.541 + elif isinstance(n, compiler.ast.While):
10.542 + self.process_while_node(n)
10.543 +
10.544 + elif isinstance(n, compiler.ast.If):
10.545 + self.process_if_node(n)
10.546 +
10.547 + elif isinstance(n, (compiler.ast.And, compiler.ast.Or)):
10.548 + return self.process_logical_node(n)
10.549 +
10.550 + # Exception control-flow tracking.
10.551 +
10.552 + elif isinstance(n, compiler.ast.TryExcept):
10.553 + self.process_try_node(n)
10.554 +
10.555 + elif isinstance(n, compiler.ast.TryFinally):
10.556 + self.process_try_finally_node(n)
10.557 +
10.558 + # Control-flow modification statements.
10.559 +
10.560 + elif isinstance(n, compiler.ast.Break):
10.561 + self.trackers[-1].suspend_broken_branch()
10.562 +
10.563 + elif isinstance(n, compiler.ast.Continue):
10.564 + self.trackers[-1].suspend_continuing_branch()
10.565 +
10.566 + elif isinstance(n, compiler.ast.Raise):
10.567 + self.process_structure(n)
10.568 + self.trackers[-1].abandon_branch()
10.569 +
10.570 + elif isinstance(n, compiler.ast.Return):
10.571 + self.process_structure(n)
10.572 + self.trackers[-1].abandon_returning_branch()
10.573 +
10.574 + # Invocations.
10.575 +
10.576 + elif isinstance(n, compiler.ast.CallFunc):
10.577 + return self.process_invocation_node(n)
10.578 +
10.579 + # Constant usage.
10.580 +
10.581 + elif isinstance(n, compiler.ast.Const):
10.582 + return self.get_literal_instance(n, n.value.__class__.__name__)
10.583 +
10.584 + elif isinstance(n, compiler.ast.Dict):
10.585 + return self.get_literal_instance(n, "dict")
10.586 +
10.587 + elif isinstance(n, compiler.ast.List):
10.588 + return self.get_literal_instance(n, "list")
10.589 +
10.590 + elif isinstance(n, compiler.ast.Tuple):
10.591 + return self.get_literal_instance(n, "tuple")
10.592 +
10.593 + # List comprehensions and if expressions.
10.594 +
10.595 + elif isinstance(n, compiler.ast.ListComp):
10.596 + self.process_listcomp_node(n)
10.597 +
10.598 + elif isinstance(n, compiler.ast.IfExp):
10.599 + self.process_ifexp_node(n)
10.600 +
10.601 + # All other nodes are processed depth-first.
10.602 +
10.603 + else:
10.604 + self.process_structure(n)
10.605 +
10.606 + # By default, no expression details are returned.
10.607 +
10.608 + return None
10.609 +
10.610 + # Specific node handling.
10.611 +
10.612 + def process_assignment_node(self, n, expr):
10.613 +
10.614 + "Process the individual node 'n' to be assigned the contents of 'expr'."
10.615 +
10.616 + # Names and attributes are assigned the entire expression.
10.617 +
10.618 + if isinstance(n, compiler.ast.AssName):
10.619 +
10.620 + name_ref = expr and self.process_structure_node(expr)
10.621 +
10.622 + # Name assignments populate either function namespaces or the
10.623 + # general namespace hierarchy.
10.624 +
10.625 + self.assign_general_local(n.name, name_ref)
10.626 +
10.627 + # Record usage of the name.
10.628 +
10.629 + self.record_name(n.name)
10.630 +
10.631 + elif isinstance(n, compiler.ast.AssAttr):
10.632 + if expr: self.process_structure_node(expr)
10.633 + self.process_attribute_access(n)
10.634 +
10.635 + # Lists and tuples are matched against the expression and their
10.636 + # items assigned to expression items.
10.637 +
10.638 + elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)):
10.639 + self.process_assignment_node_items(n, expr)
10.640 +
10.641 + # Slices and subscripts are permitted within assignment nodes.
10.642 +
10.643 + elif isinstance(n, compiler.ast.Slice):
10.644 + self.process_slice_node(n, expr)
10.645 +
10.646 + elif isinstance(n, compiler.ast.Subscript):
10.647 + self.process_subscript_node(n, expr)
10.648 +
10.649 + def process_attribute_access(self, n):
10.650 +
10.651 + "Process the given attribute access node 'n'."
10.652 +
10.653 + # Obtain any completed chain and return the reference to it.
10.654 +
10.655 + name_ref = self.process_attribute_chain(n)
10.656 + if self.have_access_expression(n):
10.657 + return name_ref
10.658 +
10.659 + # Where the start of the chain of attributes has been reached, determine
10.660 + # the complete access.
10.661 +
10.662 + # Given a non-access node, this chain can be handled in its entirety,
10.663 + # either being name-based and thus an access rooted on a name, or being
10.664 + # based on some other node and thus an anonymous access of some kind.
10.665 +
10.666 + path = self.get_namespace_path()
10.667 +
10.668 + # Start with the the full attribute chain.
10.669 +
10.670 + remaining = self.attrs
10.671 + attrnames = ".".join(remaining)
10.672 +
10.673 + # If the accessor cannot be identified, or where attributes
10.674 + # remain in an attribute chain, record the anonymous accesses.
10.675 +
10.676 + if not isinstance(name_ref, NameRef): # includes ResolvedNameRef
10.677 +
10.678 + assignment = isinstance(n, compiler.ast.AssAttr)
10.679 +
10.680 + init_item(self.attr_accesses, path, set)
10.681 + self.attr_accesses[path].add(attrnames)
10.682 +
10.683 + self.record_access_details(None, attrnames, assignment)
10.684 + del self.attrs[0]
10.685 + return
10.686 +
10.687 + # Name-based accesses will handle the first attribute in a
10.688 + # chain.
10.689 +
10.690 + else:
10.691 + attrname = remaining[0]
10.692 +
10.693 + # Attribute assignments are used to identify instance attributes.
10.694 +
10.695 + if isinstance(n, compiler.ast.AssAttr) and \
10.696 + self.in_class and self.in_function and n.expr.name == "self":
10.697 +
10.698 + self.set_instance_attr(attrname)
10.699 +
10.700 + # Record attribute usage using any name local to this namespace,
10.701 + # if assigned in the namespace, or using an external name
10.702 + # (presently just globals within classes).
10.703 +
10.704 + name = self.get_name_for_tracking(name_ref.name, name_ref.final())
10.705 + tracker = self.trackers[-1]
10.706 +
10.707 + immediate_access = len(self.attrs) == 1
10.708 + assignment = immediate_access and isinstance(n, compiler.ast.AssAttr)
10.709 +
10.710 + del self.attrs[0]
10.711 +
10.712 + # Record global-based chains for subsequent resolution.
10.713 +
10.714 + is_global = self.in_function and not self.function_locals[path].has_key(name) or \
10.715 + not self.in_function
10.716 +
10.717 + if is_global:
10.718 + self.record_global_access_details(name, attrnames)
10.719 +
10.720 + # Make sure the name is being tracked: global names will not
10.721 + # already be initialised in a branch and must be added
10.722 + # explicitly.
10.723 +
10.724 + if not tracker.have_name(name):
10.725 + tracker.assign_names([name])
10.726 + if self.in_function:
10.727 + self.scope_globals[path].add(name)
10.728 +
10.729 + # Record attribute usage in the tracker, and record the branch
10.730 + # information for the access.
10.731 +
10.732 + branches = tracker.use_attribute(name, attrname)
10.733 +
10.734 + if not branches:
10.735 + print >>sys.stderr, "In %s, name %s is accessed using %s before an assignment." % (
10.736 + path, name, attrname)
10.737 + branches = tracker.use_attribute(name, attrname)
10.738 +
10.739 + self.record_branches_for_access(branches, name, attrnames)
10.740 + access_number = self.record_access_details(name, attrnames, assignment)
10.741 + return AccessRef(name, attrnames, access_number)
10.742 +
10.743 + def process_class_node(self, n):
10.744 +
10.745 + "Process the given class node 'n'."
10.746 +
10.747 + path = self.get_namespace_path()
10.748 +
10.749 + # To avoid notions of class "versions" where the same definition
10.750 + # might be parameterised with different state and be referenced
10.751 + # elsewhere (as base classes, for example), classes in functions or
10.752 + # conditions are forbidden.
10.753 +
10.754 + if self.in_function or self.in_conditional:
10.755 + print >>sys.stderr, "In %s, class %s in function or conditional statement ignored." % (
10.756 + path, n.name)
10.757 + return
10.758 +
10.759 + # Resolve base classes.
10.760 +
10.761 + bases = []
10.762 +
10.763 + for base in n.bases:
10.764 + base_class = self.get_class(base)
10.765 +
10.766 + if not base_class:
10.767 + print >>sys.stderr, "In %s, class %s has unidentifiable base classes." % (
10.768 + path, n.name)
10.769 + return
10.770 + else:
10.771 + bases.append(base_class)
10.772 +
10.773 + # Record bases for the class and retain the class name.
10.774 +
10.775 + class_name = self.get_object_path(n.name)
10.776 +
10.777 + if not bases and class_name != "__builtins__.core.object":
10.778 + ref = self.get_object("__builtins__.object")
10.779 + bases.append(ref)
10.780 +
10.781 + self.importer.classes[class_name] = self.classes[class_name] = bases
10.782 + self.importer.subclasses[class_name] = set()
10.783 + self.scope_globals[class_name] = set()
10.784 +
10.785 + # Set the definition before entering the namespace rather than
10.786 + # afterwards because methods may reference it. In normal Python,
10.787 + # a class is not accessible until the definition is complete, but
10.788 + # methods can generally reference it since upon being called the
10.789 + # class will already exist.
10.790 +
10.791 + self.set_definition(n.name, "<class>")
10.792 +
10.793 + in_class = self.in_class
10.794 + self.in_class = class_name
10.795 + self.set_instance_attr("__class__", Reference("<class>", class_name))
10.796 + self.enter_namespace(n.name)
10.797 + self.set_name("__fn__") # special instantiator attribute
10.798 + self.set_name("__args__") # special instantiator attribute
10.799 + self.assign_general_local("__name__", self.get_constant("str", class_name))
10.800 + self.process_structure_node(n.code)
10.801 + self.exit_namespace()
10.802 + self.in_class = in_class
10.803 +
10.804 + def process_from_node(self, n):
10.805 +
10.806 + "Process the given node 'n', importing from another module."
10.807 +
10.808 + path = self.get_namespace_path()
10.809 +
10.810 + modname, names = self.get_module_name(n)
10.811 +
10.812 + # Load the mentioned module.
10.813 +
10.814 + top, module = self.get_module(modname, True)
10.815 + self.set_module(None, module, hidden=True)
10.816 +
10.817 + if not module:
10.818 + print >>sys.stderr, "In %s, from statement importing from %s failed." % (
10.819 + path, modname)
10.820 +
10.821 + # Attempt to obtain the referenced objects.
10.822 +
10.823 + for name, alias in n.names:
10.824 +
10.825 + # NOTE: Package submodules are not implicitly imported.
10.826 +
10.827 + if name == "*":
10.828 + if module:
10.829 +
10.830 + # Warn about a circular import that probably doesn't find
10.831 + # the names.
10.832 +
10.833 + if not module.loaded:
10.834 + print >>sys.stderr, "In %s, from statement performs circular import %s of %s." % (
10.835 + path, modname)
10.836 +
10.837 + for name, value in module.get_globals().items():
10.838 + if name != "__name__":
10.839 + value = ResolvedNameRef(name, value)
10.840 + self.set_general_local(name, value)
10.841 + self.set_imported_name(name, modname)
10.842 + break
10.843 +
10.844 + # Explicit names.
10.845 +
10.846 + ref = self.import_name_from_module(name, modname, module, alias)
10.847 + value = ResolvedNameRef(alias or name, ref)
10.848 + self.set_general_local(alias or name, value)
10.849 + self.set_imported_name(name, modname, alias)
10.850 +
10.851 + def import_name_from_module(self, name, modname, module, alias=None):
10.852 +
10.853 + """
10.854 + Import 'name' from the module having the given 'modname', with 'module'
10.855 + having been obtained for the module name, using 'alias' for the imported
10.856 + name in the current namespace.
10.857 + """
10.858 +
10.859 + path = self.get_namespace_path()
10.860 +
10.861 + if module and module.get_global(name):
10.862 + value = module.get_global(name)
10.863 +
10.864 + # Warn about an import that fails to provide a name, perhaps due
10.865 + # to a circular import.
10.866 +
10.867 + if not value:
10.868 + print >>sys.stderr, "In %s, from statement cannot import %s from %s%s." % (
10.869 + path, name, modname, not module.loaded and "(circular import)")
10.870 +
10.871 + return value
10.872 +
10.873 + # Record the name as a dependency.
10.874 +
10.875 + else:
10.876 + return Reference("<depends>", "%s.%s" % (modname, name))
10.877 +
10.878 + def process_function_node(self, n, name):
10.879 +
10.880 + """
10.881 + Process the given function or lambda node 'n' with the given 'name'.
10.882 + """
10.883 +
10.884 + is_lambda = isinstance(n, compiler.ast.Lambda)
10.885 +
10.886 + # Where a function is declared conditionally, use a separate name for
10.887 + # the definition, and assign the definition to the stated name.
10.888 +
10.889 + if (self.in_conditional or self.in_function) and not is_lambda:
10.890 + original_name = name
10.891 + name = self.get_lambda_name()
10.892 + else:
10.893 + original_name = None
10.894 +
10.895 + # Initialise argument and local records.
10.896 +
10.897 + function_name = self.get_object_path(name)
10.898 +
10.899 + argnames = self.importer.function_parameters[function_name] = \
10.900 + self.function_parameters[function_name] = get_argnames(n.argnames)
10.901 + locals = self.function_locals[function_name] = {}
10.902 +
10.903 + for argname in argnames:
10.904 + locals[argname] = Reference("<var>")
10.905 +
10.906 + globals = self.scope_globals[function_name] = set()
10.907 +
10.908 + # Process the defaults.
10.909 +
10.910 + defaults = self.importer.function_defaults[function_name] = \
10.911 + self.function_defaults[function_name] = []
10.912 +
10.913 + for argname, default in compiler.ast.get_defaults(n):
10.914 + if default:
10.915 +
10.916 + # Obtain any reference for the default.
10.917 +
10.918 + name_ref = self.process_structure_node(default)
10.919 + defaults.append((argname, name_ref.is_name() and name_ref.reference() or Reference("<var>")))
10.920 +
10.921 + # Reset conditional tracking to focus on the function contents.
10.922 +
10.923 + parent_function = self.parent_function
10.924 + self.parent_function = self.in_function and self.get_namespace_path() or None
10.925 +
10.926 + in_conditional = self.in_conditional
10.927 + self.in_conditional = False
10.928 +
10.929 + in_function = self.in_function
10.930 + self.in_function = function_name
10.931 +
10.932 + self.enter_namespace(name)
10.933 +
10.934 + # Track attribute usage within the namespace.
10.935 +
10.936 + path = self.get_namespace_path()
10.937 + init_item(self.propagated_names, path, set)
10.938 +
10.939 + self.start_tracking(locals)
10.940 + self.process_structure_node(n.code)
10.941 + self.stop_tracking()
10.942 +
10.943 + # Propagate names from parent scopes.
10.944 +
10.945 + for local in self.propagated_names[path]:
10.946 + if not local in argnames and self.trackers[-1].have_name(local):
10.947 + argnames.append(local)
10.948 + defaults.append((local, Reference("<var>")))
10.949 + self.set_function_local(local)
10.950 +
10.951 + # Exit to the parent and note propagated names.
10.952 +
10.953 + self.exit_namespace()
10.954 +
10.955 + parent = self.get_namespace_path()
10.956 + if self.propagated_names.has_key(parent):
10.957 + for local in self.propagated_names[path]:
10.958 + self.propagated_names[parent].add(local)
10.959 +
10.960 + # Update flags.
10.961 +
10.962 + self.in_function = in_function
10.963 + self.in_conditional = in_conditional
10.964 + self.parent_function = parent_function
10.965 +
10.966 + # Define the function using the appropriate name.
10.967 +
10.968 + self.set_definition(name, "<function>")
10.969 +
10.970 + # Where a function is set conditionally, assign the name.
10.971 +
10.972 + if original_name:
10.973 + self.process_assignment_for_function(original_name, name)
10.974 +
10.975 + def process_global_node(self, n):
10.976 +
10.977 + """
10.978 + Process the given "global" node 'n'.
10.979 + """
10.980 +
10.981 + path = self.get_namespace_path()
10.982 +
10.983 + if path != self.name:
10.984 + self.scope_globals[path].update(n.names)
10.985 +
10.986 + def process_if_node(self, n):
10.987 +
10.988 + """
10.989 + Process the given "if" node 'n'.
10.990 + """
10.991 +
10.992 + tracker = self.trackers[-1]
10.993 + tracker.new_branchpoint()
10.994 +
10.995 + for test, body in n.tests:
10.996 + self.process_structure_node(test)
10.997 +
10.998 + tracker.new_branch()
10.999 +
10.1000 + in_conditional = self.in_conditional
10.1001 + self.in_conditional = True
10.1002 + self.process_structure_node(body)
10.1003 + self.in_conditional = in_conditional
10.1004 +
10.1005 + tracker.shelve_branch()
10.1006 +
10.1007 + # Maintain a branch for the else clause.
10.1008 +
10.1009 + tracker.new_branch()
10.1010 + if n.else_:
10.1011 + self.process_structure_node(n.else_)
10.1012 + tracker.shelve_branch()
10.1013 +
10.1014 + tracker.merge_branches()
10.1015 +
10.1016 + def process_ifexp_node(self, n):
10.1017 +
10.1018 + "Process the given if expression node 'n'."
10.1019 +
10.1020 + name_ref = self.process_structure_node(self.convert_ifexp_node(n))
10.1021 +
10.1022 + path = self.get_namespace_path()
10.1023 + self.allocate_arguments(path, self.function_defaults[name_ref.get_origin()])
10.1024 + self.deallocate_arguments(path, self.function_defaults[name_ref.get_origin()])
10.1025 +
10.1026 + return InvocationRef(name_ref)
10.1027 +
10.1028 + def process_import_node(self, n):
10.1029 +
10.1030 + "Process the given import node 'n'."
10.1031 +
10.1032 + path = self.get_namespace_path()
10.1033 +
10.1034 + # Load the mentioned module.
10.1035 +
10.1036 + for name, alias in n.names:
10.1037 + module, leaf_module = self.get_module(name, alias)
10.1038 +
10.1039 + if not module:
10.1040 + print >>sys.stderr, "In %s, import statement importing from %s failed." % (
10.1041 + path, name)
10.1042 + if module and not module.loaded:
10.1043 + print >>sys.stderr, "In %s, import statement performs circular import of %s." % (
10.1044 + path, name)
10.1045 +
10.1046 + self.set_module(alias or name.split(".")[0], module, leaf_module)
10.1047 +
10.1048 + def process_invocation_node(self, n):
10.1049 +
10.1050 + "Process the given invocation node 'n'."
10.1051 +
10.1052 + path = self.get_namespace_path()
10.1053 +
10.1054 + self.allocate_arguments(path, n.args)
10.1055 +
10.1056 + try:
10.1057 + # Process the expression, obtaining any identified reference.
10.1058 +
10.1059 + name_ref = self.process_structure_node(n.node)
10.1060 +
10.1061 + # Process the arguments.
10.1062 +
10.1063 + for arg in n.args:
10.1064 + self.process_structure_node(arg)
10.1065 +
10.1066 + # Detect class invocations.
10.1067 +
10.1068 + if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind("<class>"):
10.1069 + return InstanceRef(name_ref.reference().instance_of())
10.1070 +
10.1071 + elif isinstance(name_ref, NameRef):
10.1072 + return InvocationRef(name_ref)
10.1073 +
10.1074 + return None
10.1075 +
10.1076 + finally:
10.1077 + self.deallocate_arguments(path, n.args)
10.1078 +
10.1079 + def process_lambda_node(self, n):
10.1080 +
10.1081 + "Process the given lambda node 'n'."
10.1082 +
10.1083 + name = self.get_lambda_name()
10.1084 + self.process_function_node(n, name)
10.1085 +
10.1086 + origin = self.get_object_path(name)
10.1087 + return ResolvedNameRef(name, Reference("<function>", origin))
10.1088 +
10.1089 + def process_listcomp_node(self, n):
10.1090 +
10.1091 + "Process the given list comprehension node 'n'."
10.1092 +
10.1093 + name_ref = self.process_structure_node(self.convert_listcomp_node(n))
10.1094 +
10.1095 + path = self.get_namespace_path()
10.1096 + self.allocate_arguments(path, self.function_defaults[name_ref.get_origin()])
10.1097 + self.deallocate_arguments(path, self.function_defaults[name_ref.get_origin()])
10.1098 +
10.1099 + return InvocationRef(name_ref)
10.1100 +
10.1101 + def process_logical_node(self, n):
10.1102 +
10.1103 + "Process the given operator node 'n'."
10.1104 +
10.1105 + self.process_operator_chain(n.nodes, self.process_structure_node)
10.1106 +
10.1107 + def process_name_node(self, n):
10.1108 +
10.1109 + "Process the given name node 'n'."
10.1110 +
10.1111 + path = self.get_namespace_path()
10.1112 +
10.1113 + # Special names.
10.1114 +
10.1115 + if n.name.startswith("$"):
10.1116 + value = self.get_special(n.name)
10.1117 + if value:
10.1118 + return value
10.1119 +
10.1120 + # Special case for operator functions introduced through code
10.1121 + # transformations.
10.1122 +
10.1123 + if n.name.startswith("$op"):
10.1124 +
10.1125 + # Obtain the location of the actual function defined in the operator
10.1126 + # package.
10.1127 +
10.1128 + op = n.name[len("$op"):]
10.1129 +
10.1130 + # Access the operator module.
10.1131 +
10.1132 + top, module = self.get_module("operator", True)
10.1133 + self.set_module(None, module, hidden=True)
10.1134 +
10.1135 + # Link the operation to the operator module definition in this
10.1136 + # module.
10.1137 +
10.1138 + self.set_imported_name(op, "operator", n.name, self.name)
10.1139 +
10.1140 + # Attempt to get a reference.
10.1141 +
10.1142 + ref = self.import_name_from_module(op, "operator", module)
10.1143 + ref = self.get_object("operator.%s" % op) or ref
10.1144 +
10.1145 + # Record the imported name and provide the resolved name reference.
10.1146 +
10.1147 + value = ResolvedNameRef(n.name, ref)
10.1148 + self.set_special(n.name, value)
10.1149 + return value
10.1150 +
10.1151 + # Record usage of the name.
10.1152 +
10.1153 + self.record_name(n.name)
10.1154 +
10.1155 + # Search for unknown names in non-function scopes immediately.
10.1156 + # External names in functions are resolved later.
10.1157 +
10.1158 + ref = self.find_name(n.name)
10.1159 + if ref:
10.1160 + return ResolvedNameRef(n.name, ref)
10.1161 +
10.1162 + # Global name.
10.1163 +
10.1164 + elif self.in_function and n.name in self.scope_globals[path]:
10.1165 + return NameRef(n.name)
10.1166 +
10.1167 + # Examine other names.
10.1168 +
10.1169 + else:
10.1170 + tracker = self.trackers[-1]
10.1171 +
10.1172 + # Check local names.
10.1173 +
10.1174 + branches = tracker.tracking_name(n.name)
10.1175 +
10.1176 + # Find names inherited from a parent scope.
10.1177 +
10.1178 + if not branches and self.parent_function:
10.1179 + branches = tracker.have_name(n.name)
10.1180 + if branches:
10.1181 + self.propagate_name(n.name)
10.1182 +
10.1183 + # Local or inherited name.
10.1184 +
10.1185 + if branches:
10.1186 + self.record_branches_for_access(branches, n.name, None)
10.1187 + access_number = self.record_access_details(n.name, None, False)
10.1188 + return LocalNameRef(n.name, access_number)
10.1189 +
10.1190 + # Possible global name.
10.1191 +
10.1192 + else:
10.1193 + return NameRef(n.name)
10.1194 +
10.1195 + def process_operator_chain(self, nodes, fn):
10.1196 +
10.1197 + """
10.1198 + Process the given chain of 'nodes', applying 'fn' to each node or item.
10.1199 + Each node starts a new conditional region, effectively making a deeply-
10.1200 + nested collection of if-like statements.
10.1201 + """
10.1202 +
10.1203 + tracker = self.trackers[-1]
10.1204 +
10.1205 + for item in nodes:
10.1206 + tracker.new_branchpoint()
10.1207 + tracker.new_branch()
10.1208 + fn(item)
10.1209 +
10.1210 + for item in nodes[:-1]:
10.1211 + tracker.shelve_branch()
10.1212 + tracker.new_branch()
10.1213 + tracker.shelve_branch()
10.1214 + tracker.merge_branches()
10.1215 +
10.1216 + tracker.shelve_branch()
10.1217 + tracker.merge_branches()
10.1218 +
10.1219 + def process_try_node(self, n):
10.1220 +
10.1221 + """
10.1222 + Process the given "try...except" node 'n'.
10.1223 + """
10.1224 +
10.1225 + tracker = self.trackers[-1]
10.1226 + tracker.new_branchpoint()
10.1227 +
10.1228 + self.process_structure_node(n.body)
10.1229 +
10.1230 + for name, var, handler in n.handlers:
10.1231 + if name is not None:
10.1232 + self.process_structure_node(name)
10.1233 +
10.1234 + # Any abandoned branches from the body can now be resumed in a new
10.1235 + # branch.
10.1236 +
10.1237 + tracker.resume_abandoned_branches()
10.1238 +
10.1239 + # Establish the local for the handler.
10.1240 +
10.1241 + if var is not None:
10.1242 + self.process_structure_node(var)
10.1243 + if handler is not None:
10.1244 + self.process_structure_node(handler)
10.1245 +
10.1246 + tracker.shelve_branch()
10.1247 +
10.1248 + # The else clause maintains the usage from the body but without the
10.1249 + # abandoned branches since they would never lead to the else clause
10.1250 + # being executed.
10.1251 +
10.1252 + if n.else_:
10.1253 + tracker.new_branch()
10.1254 + self.process_structure_node(n.else_)
10.1255 + tracker.shelve_branch()
10.1256 +
10.1257 + # Without an else clause, a null branch propagates the successful
10.1258 + # outcome.
10.1259 +
10.1260 + else:
10.1261 + tracker.new_branch()
10.1262 + tracker.shelve_branch()
10.1263 +
10.1264 + tracker.merge_branches()
10.1265 +
10.1266 + def process_try_finally_node(self, n):
10.1267 +
10.1268 + """
10.1269 + Process the given "try...finally" node 'n'.
10.1270 + """
10.1271 +
10.1272 + tracker = self.trackers[-1]
10.1273 + self.process_structure_node(n.body)
10.1274 +
10.1275 + # Any abandoned branches from the body can now be resumed.
10.1276 +
10.1277 + branches = tracker.resume_all_abandoned_branches()
10.1278 + self.process_structure_node(n.final)
10.1279 +
10.1280 + # At the end of the finally clause, abandoned branches are discarded.
10.1281 +
10.1282 + tracker.restore_active_branches(branches)
10.1283 +
10.1284 + def process_while_node(self, n):
10.1285 +
10.1286 + "Process the given while node 'n'."
10.1287 +
10.1288 + tracker = self.trackers[-1]
10.1289 + tracker.new_branchpoint(loop_node=True)
10.1290 +
10.1291 + # Evaluate any test or iterator outside the loop.
10.1292 +
10.1293 + self.process_structure_node(n.test)
10.1294 +
10.1295 + # Propagate attribute usage to branches.
10.1296 +
10.1297 + tracker.new_branch(loop_node=True)
10.1298 +
10.1299 + # Enter the loop.
10.1300 +
10.1301 + in_conditional = self.in_conditional
10.1302 + self.in_conditional = True
10.1303 + self.process_structure_node(n.body)
10.1304 + self.in_conditional = in_conditional
10.1305 +
10.1306 + # Continuing branches are resumed before any test.
10.1307 +
10.1308 + tracker.resume_continuing_branches()
10.1309 +
10.1310 + # Evaluate any continuation test within the body.
10.1311 +
10.1312 + self.process_structure_node(n.test)
10.1313 +
10.1314 + tracker.shelve_branch(loop_node=True)
10.1315 +
10.1316 + # Support the non-looping condition.
10.1317 +
10.1318 + tracker.new_branch()
10.1319 + tracker.shelve_branch()
10.1320 +
10.1321 + tracker.merge_branches()
10.1322 +
10.1323 + # Evaluate any else clause outside branches.
10.1324 +
10.1325 + if n.else_:
10.1326 + self.process_structure_node(n.else_)
10.1327 +
10.1328 + # Connect broken branches to the code after any loop.
10.1329 +
10.1330 + tracker.resume_broken_branches()
10.1331 +
10.1332 + # Branch tracking methods.
10.1333 +
10.1334 + def start_tracking(self, names):
10.1335 +
10.1336 + """
10.1337 + Start tracking attribute usage for names in the current namespace,
10.1338 + immediately registering the given 'names'.
10.1339 + """
10.1340 +
10.1341 + path = self.get_namespace_path()
10.1342 + parent = self.trackers[-1]
10.1343 + tracker = BranchTracker()
10.1344 + self.trackers.append(tracker)
10.1345 +
10.1346 + # For functions created from expressions or for functions within
10.1347 + # functions, propagate usage to the new namespace.
10.1348 +
10.1349 + if self.parent_function:
10.1350 + tracker.inherit_branches(parent, names)
10.1351 +
10.1352 + # Record the given names established as new branches.
10.1353 +
10.1354 + tracker.assign_names(names)
10.1355 +
10.1356 + def assign_name(self, name, name_ref):
10.1357 +
10.1358 + "Assign to 'name' the given 'name_ref' in the current namespace."
10.1359 +
10.1360 + name = self.get_name_for_tracking(name)
10.1361 + self.trackers[-1].assign_names([name], [name_ref])
10.1362 +
10.1363 + def stop_tracking(self):
10.1364 +
10.1365 + """
10.1366 + Stop tracking attribute usage, recording computed usage for the current
10.1367 + namespace.
10.1368 + """
10.1369 +
10.1370 + path = self.get_namespace_path()
10.1371 + tracker = self.trackers.pop()
10.1372 + self.record_assignments_for_access(tracker)
10.1373 +
10.1374 + self.attr_usage[path] = tracker.get_all_usage()
10.1375 + self.name_initialisers[path] = tracker.get_all_values()
10.1376 +
10.1377 + def start_tracking_in_module(self):
10.1378 +
10.1379 + "Start tracking attribute usage in the module."
10.1380 +
10.1381 + tracker = BranchTracker()
10.1382 + self.trackers.append(tracker)
10.1383 +
10.1384 + def stop_tracking_in_module(self):
10.1385 +
10.1386 + "Stop tracking attribute usage in the module."
10.1387 +
10.1388 + tracker = self.trackers[0]
10.1389 + self.record_assignments_for_access(tracker)
10.1390 + self.attr_usage[self.name] = tracker.get_all_usage()
10.1391 + self.name_initialisers[self.name] = tracker.get_all_values()
10.1392 +
10.1393 + def propagate_name(self, name):
10.1394 +
10.1395 + "Propagate the given 'name' into the current namespace."
10.1396 +
10.1397 + path = self.get_namespace_path()
10.1398 + self.propagated_names[path].add(name)
10.1399 +
10.1400 + def record_assignments_for_access(self, tracker):
10.1401 +
10.1402 + """
10.1403 + For the current path, use the given 'tracker' to record assignment
10.1404 + version information for attribute accesses.
10.1405 + """
10.1406 +
10.1407 + path = self.get_path_for_access()
10.1408 +
10.1409 + if not self.attr_accessor_branches.has_key(path):
10.1410 + return
10.1411 +
10.1412 + init_item(self.attr_accessors, path, dict)
10.1413 + attr_accessors = self.attr_accessors[path]
10.1414 +
10.1415 + # Obtain the branches applying during each access.
10.1416 +
10.1417 + for access, all_branches in self.attr_accessor_branches[path].items():
10.1418 + name, attrnames = access
10.1419 + init_item(attr_accessors, access, list)
10.1420 +
10.1421 + # Obtain the assignments applying to each branch.
10.1422 +
10.1423 + for branches in all_branches:
10.1424 + positions = tracker.get_assignment_positions_for_branches(name, branches)
10.1425 +
10.1426 + # Detect missing name information.
10.1427 +
10.1428 + if None in positions:
10.1429 + globals = self.global_attr_accesses.get(path)
10.1430 + accesses = globals and globals.get(name)
10.1431 + if not accesses:
10.1432 + print >>sys.stderr, "In %s, %s may not be defined when used." % (
10.1433 + self.get_namespace_path(), name)
10.1434 + positions.remove(None)
10.1435 +
10.1436 + attr_accessors[access].append(positions)
10.1437 +
10.1438 + def record_branches_for_access(self, branches, name, attrnames):
10.1439 +
10.1440 + """
10.1441 + Record the given 'branches' for an access involving the given 'name' and
10.1442 + 'attrnames'.
10.1443 + """
10.1444 +
10.1445 + access = name, attrnames
10.1446 + path = self.get_path_for_access()
10.1447 +
10.1448 + init_item(self.attr_accessor_branches, path, dict)
10.1449 + attr_accessor_branches = self.attr_accessor_branches[path]
10.1450 +
10.1451 + init_item(attr_accessor_branches, access, list)
10.1452 + attr_accessor_branches[access].append(branches)
10.1453 +
10.1454 + def record_access_details(self, name, attrnames, assignment):
10.1455 +
10.1456 + """
10.1457 + For the given 'name' and 'attrnames', record an access indicating
10.1458 + whether 'assignment' is occurring.
10.1459 +
10.1460 + These details correspond to accesses otherwise recorded by the attribute
10.1461 + accessor and attribute access dictionaries.
10.1462 + """
10.1463 +
10.1464 + access = name, attrnames
10.1465 + path = self.get_path_for_access()
10.1466 +
10.1467 + init_item(self.attr_access_modifiers, path, dict)
10.1468 + init_item(self.attr_access_modifiers[path], access, list)
10.1469 +
10.1470 + access_number = len(self.attr_access_modifiers[path][access])
10.1471 + self.attr_access_modifiers[path][access].append(assignment)
10.1472 + return access_number
10.1473 +
10.1474 + def record_global_access_details(self, name, attrnames):
10.1475 +
10.1476 + """
10.1477 + Record details of a global access via the given 'name' involving the
10.1478 + indicated 'attrnames'.
10.1479 + """
10.1480 +
10.1481 + path = self.get_namespace_path()
10.1482 +
10.1483 + init_item(self.global_attr_accesses, path, dict)
10.1484 + init_item(self.global_attr_accesses[path], name, set)
10.1485 + self.global_attr_accesses[path][name].add(attrnames)
10.1486 +
10.1487 + # Namespace modification.
10.1488 +
10.1489 + def record_name(self, name):
10.1490 +
10.1491 + "Record the use of 'name' in a namespace."
10.1492 +
10.1493 + path = self.get_namespace_path()
10.1494 + init_item(self.names_used, path, set)
10.1495 + self.names_used[path].add(name)
10.1496 +
10.1497 + def set_module(self, name, module, leaf_module=None, hidden=False):
10.1498 +
10.1499 + """
10.1500 + Set a module in the current namespace using the given 'name' and
10.1501 + corresponding 'module' object, with the 'leaf_module' being recorded
10.1502 + if different. If 'hidden' is a true value, the modules are recorded as
10.1503 + not necessarily being exposed by this module. This module is, however,
10.1504 + recorded as accessing the given modules and is thus dependent on them.
10.1505 + """
10.1506 +
10.1507 + if name:
10.1508 + self.set_general_local(name, module and Reference("<module>", module.name) or None)
10.1509 + if module:
10.1510 + if hidden:
10.1511 + self.imported_hidden.add(module)
10.1512 + if leaf_module and leaf_module is not module:
10.1513 + self.imported_hidden.add(leaf_module)
10.1514 + else:
10.1515 + self.imported.add(module)
10.1516 + module.accessing_modules.add(self.name)
10.1517 + if leaf_module and leaf_module is not module:
10.1518 + self.imported.add(leaf_module)
10.1519 + leaf_module.accessing_modules.add(self.name)
10.1520 +
10.1521 + def set_definition(self, name, kind):
10.1522 +
10.1523 + """
10.1524 + Set the definition having the given 'name' and 'kind'.
10.1525 +
10.1526 + Definitions are set in the static namespace hierarchy, but they can also
10.1527 + be recorded for function locals.
10.1528 + """
10.1529 +
10.1530 + if self.is_global(name):
10.1531 + print >>sys.stderr, "In %s, %s is defined as being global." % (
10.1532 + self.get_namespace_path(), name)
10.1533 +
10.1534 + path = self.get_object_path(name)
10.1535 + self.set_object(path, kind)
10.1536 +
10.1537 + ref = self.get_object(path)
10.1538 + if ref.get_kind() == "<var>":
10.1539 + print >>sys.stderr, "In %s, %s is defined more than once." % (
10.1540 + self.get_namespace_path(), name)
10.1541 +
10.1542 + if not self.is_global(name) and self.in_function:
10.1543 + self.set_function_local(name, ref)
10.1544 +
10.1545 + def set_function_local(self, name, ref=None):
10.1546 +
10.1547 + "Set the local with the given 'name' and optional 'ref'."
10.1548 +
10.1549 + locals = self.function_locals[self.get_namespace_path()]
10.1550 + multiple = not ref or locals.has_key(name) and locals[name] != ref
10.1551 + locals[name] = multiple and Reference("<var>") or ref
10.1552 +
10.1553 + def assign_general_local(self, name, name_ref):
10.1554 +
10.1555 + """
10.1556 + Set for 'name' the given 'name_ref', recording the name for attribute
10.1557 + usage tracking.
10.1558 + """
10.1559 +
10.1560 + self.set_general_local(name, name_ref)
10.1561 + self.assign_name(name, name_ref)
10.1562 +
10.1563 + def set_general_local(self, name, value=None):
10.1564 +
10.1565 + """
10.1566 + Set the 'name' with optional 'value' in any kind of local namespace,
10.1567 + where the 'value' should be a reference if specified.
10.1568 + """
10.1569 +
10.1570 + init_value = self.get_initialising_value(value)
10.1571 +
10.1572 + # Module global names.
10.1573 +
10.1574 + if self.is_global(name):
10.1575 + path = self.get_global_path(name)
10.1576 + self.set_object(path, init_value)
10.1577 +
10.1578 + # Function local names.
10.1579 +
10.1580 + elif self.in_function:
10.1581 + path = self.get_object_path(name)
10.1582 + self.set_function_local(name, init_value)
10.1583 +
10.1584 + # Other namespaces (classes).
10.1585 +
10.1586 + else:
10.1587 + path = self.get_object_path(name)
10.1588 + self.set_name(name, init_value)
10.1589 +
10.1590 + def set_name(self, name, ref=None):
10.1591 +
10.1592 + "Attach the 'name' with optional 'ref' to the current namespace."
10.1593 +
10.1594 + self.set_object(self.get_object_path(name), ref)
10.1595 +
10.1596 + def set_instance_attr(self, name, ref=None):
10.1597 +
10.1598 + """
10.1599 + Add an instance attribute of the given 'name' to the current class,
10.1600 + using the optional 'ref'.
10.1601 + """
10.1602 +
10.1603 + init_item(self.instance_attrs, self.in_class, set)
10.1604 + self.instance_attrs[self.in_class].add(name)
10.1605 +
10.1606 + if ref:
10.1607 + init_item(self.instance_attr_constants, self.in_class, dict)
10.1608 + self.instance_attr_constants[self.in_class][name] = ref
10.1609 +
10.1610 + def get_initialising_value(self, value):
10.1611 +
10.1612 + "Return a suitable initialiser reference for 'value'."
10.1613 +
10.1614 + if isinstance(value, (NameRef, AccessRef, InstanceRef)): # includes LiteralSequenceRef, ResolvedNameRef
10.1615 + return value.reference()
10.1616 +
10.1617 + # In general, invocations do not produce known results. However, the
10.1618 + # name initialisers are resolved once a module has been inspected.
10.1619 +
10.1620 + elif isinstance(value, InvocationRef):
10.1621 + return None
10.1622 +
10.1623 + else:
10.1624 + return value
10.1625 +
10.1626 + # Static, program-relative naming.
10.1627 +
10.1628 + def find_name(self, name):
10.1629 +
10.1630 + """
10.1631 + Return the qualified name for the given 'name' used in the current
10.1632 + non-function namespace.
10.1633 + """
10.1634 +
10.1635 + path = self.get_namespace_path()
10.1636 + ref = None
10.1637 +
10.1638 + if not self.in_function and name not in predefined_constants:
10.1639 + if self.in_class:
10.1640 + ref = self.get_object(self.get_object_path(name))
10.1641 + if not ref:
10.1642 + ref = self.get_global_or_builtin(name)
10.1643 +
10.1644 + return ref
10.1645 +
10.1646 + def get_class(self, node):
10.1647 +
10.1648 + """
10.1649 + Use the given 'node' to obtain the identity of a class. Return a
10.1650 + reference for the class. Unresolved dependencies are permitted and must
10.1651 + be resolved later.
10.1652 + """
10.1653 +
10.1654 + ref = self._get_class(node)
10.1655 + return ref.has_kind(["<class>", "<depends>"]) and ref or None
10.1656 +
10.1657 + def _get_class(self, node):
10.1658 +
10.1659 + """
10.1660 + Use the given 'node' to find a class definition. Return a reference to
10.1661 + the class.
10.1662 + """
10.1663 +
10.1664 + if isinstance(node, compiler.ast.Getattr):
10.1665 +
10.1666 + # Obtain the identity of the access target.
10.1667 +
10.1668 + ref = self._get_class(node.expr)
10.1669 +
10.1670 + # Where the target is a class or module, obtain the identity of the
10.1671 + # attribute.
10.1672 +
10.1673 + if ref.has_kind(["<function>", "<var>"]):
10.1674 + return None
10.1675 + else:
10.1676 + attrname = "%s.%s" % (ref.get_origin(), node.attrname)
10.1677 + return self.get_object(attrname)
10.1678 +
10.1679 + # Names can be module-level or built-in.
10.1680 +
10.1681 + elif isinstance(node, compiler.ast.Name):
10.1682 +
10.1683 + # Record usage of the name and attempt to identify it.
10.1684 +
10.1685 + self.record_name(node.name)
10.1686 + return self.get_global_or_builtin(node.name)
10.1687 + else:
10.1688 + return None
10.1689 +
10.1690 + def get_constant(self, name, value):
10.1691 +
10.1692 + "Return a constant reference for the given type 'name' and 'value'."
10.1693 +
10.1694 + ref = self.get_literal_builtin(name)
10.1695 + return self.get_constant_reference(ref, value)
10.1696 +
10.1697 + def get_literal_instance(self, n, name):
10.1698 +
10.1699 + "For node 'n', return a reference to an instance of 'name'."
10.1700 +
10.1701 + # Get a class reference.
10.1702 +
10.1703 + ref = self.get_literal_builtin(name)
10.1704 +
10.1705 + # Obtain the details of the literal itself.
10.1706 + # An alias to the type is generated for sequences.
10.1707 +
10.1708 + if name in ("dict", "list", "tuple"):
10.1709 + self.set_special_literal(name, ref)
10.1710 + return self.process_literal_sequence_node(n, name, ref, LiteralSequenceRef)
10.1711 +
10.1712 + # Constant values are independently recorded.
10.1713 +
10.1714 + else:
10.1715 + return self.get_constant_reference(ref, n.value)
10.1716 +
10.1717 + def get_literal_builtin(self, name):
10.1718 +
10.1719 + "Return a reference for a built-in literal type of the given 'name'."
10.1720 +
10.1721 + ref = self.get_builtin(name)
10.1722 + true_origin = "__builtins__.%s.%s" % (name, name)
10.1723 + exposed_origin = "__builtins__.%s" % name
10.1724 +
10.1725 + # Obtain fully-imported built-in class references.
10.1726 +
10.1727 + if ref and ref.has_kind("<class>"):
10.1728 + pass
10.1729 +
10.1730 + # Early-stage references need explicit references.
10.1731 +
10.1732 + elif ref:
10.1733 + ref = Reference("<class>", true_origin)
10.1734 +
10.1735 + # Otherwise, the normal locations can be used.
10.1736 +
10.1737 + else:
10.1738 + ref = Reference("<class>", true_origin, exposed_origin)
10.1739 +
10.1740 + return ref
10.1741 +
10.1742 + # Functions and invocations.
10.1743 +
10.1744 + def allocate_arguments(self, path, args):
10.1745 +
10.1746 + """
10.1747 + Allocate temporary argument storage using current and maximum
10.1748 + requirements for the given 'path' and 'args'.
10.1749 + """
10.1750 +
10.1751 + init_item(self.function_targets, path, lambda: [0, 0])
10.1752 + t = self.function_targets[path]
10.1753 + t[0] += 1
10.1754 + t[1] = max(t[0], t[1])
10.1755 +
10.1756 + init_item(self.function_arguments, path, lambda: [0, 0])
10.1757 + t = self.function_arguments[path]
10.1758 + t[0] += len(args) + 1
10.1759 + t[1] = max(t[0], t[1])
10.1760 +
10.1761 + def deallocate_arguments(self, path, args):
10.1762 +
10.1763 + "Deallocate temporary argument storage for the given 'path' and 'args'."
10.1764 +
10.1765 + self.function_targets[path][0] -= 1
10.1766 + self.function_arguments[path][0] -= len(args) + 1
10.1767 +
10.1768 +# vim: tabstop=4 expandtab shiftwidth=4
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/internal_tests/branches.py Tue Aug 30 16:51:10 2016 +0200
11.3 @@ -0,0 +1,627 @@
11.4 +#!/usr/bin/env python
11.5 +
11.6 +import branching
11.7 +
11.8 +names = []
11.9 +
11.10 +# Equivalent to...
11.11 +#
11.12 +# a = ...
11.13 +# a.p
11.14 +# if ...:
11.15 +# a = ...
11.16 +# a.x
11.17 +# else:
11.18 +# ...
11.19 +# a.q
11.20 +
11.21 +bt = branching.BranchTracker()
11.22 +a1 = bt.assign_names(["a"])
11.23 +bt.use_attribute("a", "p")
11.24 +bt.new_branchpoint() # begin
11.25 +bt.new_branch() # if ...
11.26 +a2 = bt.assign_names(["a"]) # a = ...
11.27 +ax = bt.use_attribute("a", "x")
11.28 +bt.shelve_branch()
11.29 +bt.new_branch() # else
11.30 +bt.shelve_branch()
11.31 +bt.merge_branches() # end
11.32 +aq = bt.use_attribute("a", "q")
11.33 +
11.34 +print a1.get_usage() == \
11.35 + {'a' : set([('p',), ('p', 'q')])}, \
11.36 + a1.get_usage()
11.37 +print a2.get_usage() == \
11.38 + {'a' : set([('q', 'x')])}, \
11.39 + a2.get_usage()
11.40 +print bt.get_assignment_positions_for_branches("a", ax) == [1], \
11.41 + bt.get_assignment_positions_for_branches("a", ax)
11.42 +print bt.get_assignment_positions_for_branches("a", aq) == [0, 1], \
11.43 + bt.get_assignment_positions_for_branches("a", aq)
11.44 +names.append(bt.assignments["a"])
11.45 +
11.46 +# Equivalent to...
11.47 +#
11.48 +# a = ...
11.49 +# a.p
11.50 +# if ...:
11.51 +# a.x
11.52 +# elif ...:
11.53 +# a.y; a.z
11.54 +# else:
11.55 +# ...
11.56 +# a.q
11.57 +
11.58 +bt = branching.BranchTracker()
11.59 +a = bt.assign_names(["a"])
11.60 +bt.use_attribute("a", "p")
11.61 +bt.new_branchpoint() # begin
11.62 +bt.new_branch() # if ...
11.63 +ax = bt.use_attribute("a", "x")
11.64 +bt.shelve_branch()
11.65 +bt.new_branch() # elif ...
11.66 +ay = bt.use_attribute("a", "y")
11.67 +az = bt.use_attribute("a", "z")
11.68 +bt.shelve_branch()
11.69 +bt.new_branch() # else
11.70 +bt.shelve_branch()
11.71 +bt.merge_branches() # end
11.72 +bt.use_attribute("a", "q")
11.73 +
11.74 +print a.get_usage() == \
11.75 + {'a' : set([('p', 'q'), ('p', 'q', 'x'), ('p', 'q', 'y', 'z')])}, \
11.76 + a.get_usage()
11.77 +print bt.get_assignment_positions_for_branches("a", ax) == [0], \
11.78 + bt.get_assignment_positions_for_branches("a", ax)
11.79 +print bt.get_assignment_positions_for_branches("a", ay) == [0], \
11.80 + bt.get_assignment_positions_for_branches("a", ay)
11.81 +print bt.get_assignment_positions_for_branches("a", az) == [0], \
11.82 + bt.get_assignment_positions_for_branches("a", az)
11.83 +names.append(bt.assignments["a"])
11.84 +
11.85 +# Equivalent to...
11.86 +#
11.87 +# a = ...
11.88 +# a.p
11.89 +# while ...:
11.90 +# a.x
11.91 +# a.q
11.92 +
11.93 +bt = branching.BranchTracker()
11.94 +a = bt.assign_names(["a"])
11.95 +bt.use_attribute("a", "p")
11.96 +bt.new_branchpoint(True) # begin
11.97 +bt.new_branch(True) # while ...
11.98 +ax = bt.use_attribute("a", "x")
11.99 +bt.resume_continuing_branches()
11.100 +bt.shelve_branch(True)
11.101 +bt.new_branch() # (null)
11.102 +bt.shelve_branch()
11.103 +bt.merge_branches() # end
11.104 +bt.resume_broken_branches()
11.105 +bt.use_attribute("a", "q")
11.106 +
11.107 +print a.get_usage() == \
11.108 + {'a' : set([('p', 'q'), ('p', 'q', 'x')])}, a.get_usage()
11.109 +print bt.get_assignment_positions_for_branches("a", ax) == [0], \
11.110 + bt.get_assignment_positions_for_branches("a", ax)
11.111 +names.append(bt.assignments["a"])
11.112 +
11.113 +# Equivalent to...
11.114 +#
11.115 +# a = ...
11.116 +# a.p
11.117 +# while ...:
11.118 +# if ...:
11.119 +# a.x
11.120 +# else ...:
11.121 +# a.y
11.122 +# a.q
11.123 +
11.124 +bt = branching.BranchTracker()
11.125 +a = bt.assign_names(["a"])
11.126 +bt.use_attribute("a", "p")
11.127 +bt.new_branchpoint(True) # begin
11.128 +bt.new_branch(True) # while ...
11.129 +bt.new_branchpoint() # begin
11.130 +bt.new_branch() # if ...
11.131 +ax = bt.use_attribute("a", "x")
11.132 +bt.shelve_branch()
11.133 +bt.new_branch()
11.134 +ay = bt.use_attribute("a", "y")
11.135 +bt.shelve_branch()
11.136 +bt.merge_branches() # end
11.137 +bt.resume_continuing_branches()
11.138 +bt.shelve_branch(True)
11.139 +bt.new_branch() # (null)
11.140 +bt.shelve_branch()
11.141 +bt.merge_branches() # end
11.142 +bt.resume_broken_branches()
11.143 +bt.use_attribute("a", "q")
11.144 +
11.145 +print a.get_usage() == \
11.146 + {'a' : set([('p', 'q'), ('p', 'q', 'x'), ('p', 'q', 'y')])}, \
11.147 + a.get_usage()
11.148 +print bt.get_assignment_positions_for_branches("a", ax) == [0], \
11.149 + bt.get_assignment_positions_for_branches("a", ax)
11.150 +print bt.get_assignment_positions_for_branches("a", ay) == [0], \
11.151 + bt.get_assignment_positions_for_branches("a", ay)
11.152 +names.append(bt.assignments["a"])
11.153 +
11.154 +# Equivalent to...
11.155 +#
11.156 +# a = ...
11.157 +# a.p
11.158 +# while ...:
11.159 +# if ...:
11.160 +# a = ...
11.161 +# a.x
11.162 +# else ...:
11.163 +# a.y
11.164 +# a.q
11.165 +
11.166 +bt = branching.BranchTracker()
11.167 +a1 = bt.assign_names(["a"])
11.168 +bt.use_attribute("a", "p")
11.169 +bt.new_branchpoint(True) # begin
11.170 +bt.new_branch(True) # while ...
11.171 +bt.new_branchpoint() # begin
11.172 +bt.new_branch() # if ...
11.173 +a2 = bt.assign_names(["a"]) # a = ...
11.174 +ax = bt.use_attribute("a", "x")
11.175 +bt.shelve_branch()
11.176 +bt.new_branch()
11.177 +ay = bt.use_attribute("a", "y")
11.178 +bt.shelve_branch()
11.179 +bt.merge_branches() # end
11.180 +bt.resume_continuing_branches()
11.181 +bt.shelve_branch(True)
11.182 +bt.new_branch() # (null)
11.183 +bt.shelve_branch()
11.184 +bt.merge_branches() # end
11.185 +bt.resume_broken_branches()
11.186 +bt.use_attribute("a", "q")
11.187 +
11.188 +print a1.get_usage() == \
11.189 + {'a' : set([('p', 'q'), ('p', 'q', 'y'), ('p',)])}, a1.get_usage()
11.190 +print a2.get_usage() == \
11.191 + {'a' : set([('q', 'x')])}, a2.get_usage()
11.192 +print bt.get_assignment_positions_for_branches("a", ax) == [1], \
11.193 + bt.get_assignment_positions_for_branches("a", ax)
11.194 +print bt.get_assignment_positions_for_branches("a", ay) == [0, 1], \
11.195 + bt.get_assignment_positions_for_branches("a", ay)
11.196 +names.append(bt.assignments["a"])
11.197 +
11.198 +# Equivalent to...
11.199 +#
11.200 +# a = ...
11.201 +# a.p
11.202 +# while ...:
11.203 +# if ...:
11.204 +# a.y
11.205 +# else ...:
11.206 +# a = ...
11.207 +# a.x
11.208 +# a.q
11.209 +
11.210 +bt = branching.BranchTracker()
11.211 +a1 = bt.assign_names(["a"])
11.212 +bt.use_attribute("a", "p")
11.213 +bt.new_branchpoint(True) # begin
11.214 +bt.new_branch(True) # while ...
11.215 +bt.new_branchpoint() # begin
11.216 +bt.new_branch() # if ...
11.217 +ay = bt.use_attribute("a", "y")
11.218 +bt.shelve_branch()
11.219 +bt.new_branch()
11.220 +a2 = bt.assign_names(["a"]) # a = ...
11.221 +ax = bt.use_attribute("a", "x")
11.222 +bt.shelve_branch()
11.223 +bt.merge_branches() # end
11.224 +bt.resume_continuing_branches()
11.225 +bt.shelve_branch(True)
11.226 +bt.new_branch() # (null)
11.227 +bt.shelve_branch()
11.228 +bt.merge_branches() # end
11.229 +bt.resume_broken_branches()
11.230 +bt.use_attribute("a", "q")
11.231 +
11.232 +print a1.get_usage() == \
11.233 + {'a' : set([('p', 'q'), ('p', 'q', 'y'), ('p',)])}, a1.get_usage()
11.234 +print a2.get_usage() == \
11.235 + {'a' : set([('q', 'x')])}, a2.get_usage()
11.236 +print bt.get_assignment_positions_for_branches("a", ax) == [1], \
11.237 + bt.get_assignment_positions_for_branches("a", ax)
11.238 +print bt.get_assignment_positions_for_branches("a", ay) == [0, 1], \
11.239 + bt.get_assignment_positions_for_branches("a", ay)
11.240 +names.append(bt.assignments["a"])
11.241 +
11.242 +# Equivalent to...
11.243 +#
11.244 +# a = ...
11.245 +# a.p
11.246 +# while ...:
11.247 +# a = ...
11.248 +# a.x
11.249 +# a.q
11.250 +
11.251 +bt = branching.BranchTracker()
11.252 +a1 = bt.assign_names(["a"])
11.253 +ap = bt.use_attribute("a", "p")
11.254 +bt.new_branchpoint(True) # begin
11.255 +bt.new_branch(True) # while ...
11.256 +a2 = bt.assign_names(["a"]) # a = ...
11.257 +ax = bt.use_attribute("a", "x")
11.258 +bt.resume_continuing_branches()
11.259 +bt.shelve_branch(True)
11.260 +bt.new_branch() # (null)
11.261 +bt.shelve_branch()
11.262 +bt.merge_branches() # end
11.263 +bt.resume_broken_branches()
11.264 +aq = bt.use_attribute("a", "q")
11.265 +
11.266 +print a1.get_usage() == \
11.267 + {'a' : set([('p', 'q'), ('p',)])}, a1.get_usage()
11.268 +print a2.get_usage() == \
11.269 + {'a' : set([('q', 'x')])}, a2.get_usage()
11.270 +print bt.get_assignment_positions_for_branches("a", ax) == [1], \
11.271 + bt.get_assignment_positions_for_branches("a", ax)
11.272 +print bt.get_assignment_positions_for_branches("a", ap) == [0], \
11.273 + bt.get_assignment_positions_for_branches("a", ap)
11.274 +print bt.get_assignment_positions_for_branches("a", aq) == [0, 1], \
11.275 + bt.get_assignment_positions_for_branches("a", aq)
11.276 +names.append(bt.assignments["a"])
11.277 +
11.278 +# Equivalent to...
11.279 +#
11.280 +# a = ...
11.281 +# a.p
11.282 +# while ...:
11.283 +# if ...:
11.284 +# break
11.285 +# a.q
11.286 +# a.r
11.287 +
11.288 +bt = branching.BranchTracker()
11.289 +a1 = bt.assign_names(["a"])
11.290 +bt.use_attribute("a", "p")
11.291 +bt.new_branchpoint(True) # begin
11.292 +bt.new_branch(True) # while ...
11.293 +bt.new_branchpoint() # begin
11.294 +bt.new_branch() # if ...
11.295 +bt.suspend_broken_branch() # break
11.296 +bt.shelve_branch()
11.297 +bt.new_branch() # (null)
11.298 +bt.shelve_branch()
11.299 +bt.merge_branches() # end
11.300 +bt.use_attribute("a", "q")
11.301 +bt.resume_continuing_branches()
11.302 +bt.shelve_branch(True)
11.303 +bt.merge_branches() # end
11.304 +bt.resume_broken_branches()
11.305 +bt.use_attribute("a", "r")
11.306 +
11.307 +print a1.get_usage() == \
11.308 + {'a' : set([('p', 'q', 'r'), ('p', 'r')])}, a1.get_usage()
11.309 +names.append(bt.assignments["a"])
11.310 +
11.311 +# Equivalent to...
11.312 +#
11.313 +# a = ...
11.314 +# a.p and a.q and a.r
11.315 +
11.316 +bt = branching.BranchTracker()
11.317 +a1 = bt.assign_names(["a"])
11.318 +bt.new_branchpoint() # begin
11.319 +bt.new_branch()
11.320 +bt.use_attribute("a", "p")
11.321 +bt.new_branchpoint() # begin
11.322 +bt.new_branch()
11.323 +bt.use_attribute("a", "q")
11.324 +bt.new_branchpoint() # begin
11.325 +bt.new_branch()
11.326 +bt.use_attribute("a", "r")
11.327 +bt.shelve_branch()
11.328 +bt.new_branch() # (null)
11.329 +bt.shelve_branch()
11.330 +bt.merge_branches() # end
11.331 +bt.shelve_branch()
11.332 +bt.new_branch() # (null)
11.333 +bt.shelve_branch()
11.334 +bt.merge_branches() # end
11.335 +bt.shelve_branch()
11.336 +bt.merge_branches() # end
11.337 +
11.338 +print a1.get_usage() == \
11.339 + {'a' : set([('p', 'q', 'r'), ('p', 'q'), ('p',)])}, a1.get_usage()
11.340 +names.append(bt.assignments["a"])
11.341 +
11.342 +# Equivalent to...
11.343 +#
11.344 +# a = ...
11.345 +# if ...:
11.346 +# a.p
11.347 +# return
11.348 +# a.q
11.349 +
11.350 +bt = branching.BranchTracker()
11.351 +a1 = bt.assign_names(["a"])
11.352 +bt.new_branchpoint() # begin
11.353 +bt.new_branch() # if ...
11.354 +bt.use_attribute("a", "p")
11.355 +bt.abandon_returning_branch()
11.356 +bt.shelve_branch()
11.357 +bt.new_branch() # (null)
11.358 +bt.shelve_branch()
11.359 +bt.merge_branches() # end
11.360 +bt.use_attribute("a", "q")
11.361 +
11.362 +print a1.get_usage() == \
11.363 + {'a' : set([('p',), ('q',)])}, a1.get_usage()
11.364 +names.append(bt.assignments["a"])
11.365 +
11.366 +# Equivalent to...
11.367 +#
11.368 +# a = ...
11.369 +# try:
11.370 +# if ...:
11.371 +# a.p
11.372 +# return
11.373 +# a.q
11.374 +# except:
11.375 +# a.r
11.376 +
11.377 +bt = branching.BranchTracker()
11.378 +a1 = bt.assign_names(["a"])
11.379 +bt.new_branchpoint() # begin (try)
11.380 +bt.new_branchpoint() # begin
11.381 +bt.new_branch() # if ...
11.382 +bt.use_attribute("a", "p")
11.383 +bt.abandon_returning_branch()
11.384 +bt.shelve_branch() # ... if
11.385 +bt.new_branch() # (null)
11.386 +bt.shelve_branch()
11.387 +bt.merge_branches() # end
11.388 +bt.use_attribute("a", "q")
11.389 +bt.resume_abandoned_branches() # except
11.390 +bt.use_attribute("a", "r")
11.391 +bt.shelve_branch()
11.392 +bt.merge_branches() # end
11.393 +
11.394 +print a1.get_usage() == \
11.395 + {'a' : set([('p',), ('q', 'r')])}, a1.get_usage()
11.396 +names.append(bt.assignments["a"])
11.397 +
11.398 +# Equivalent to...
11.399 +#
11.400 +# a = ...
11.401 +# if ...:
11.402 +# a.p
11.403 +# a = ...
11.404 +# if ...:
11.405 +# a.q
11.406 +
11.407 +bt = branching.BranchTracker()
11.408 +a1 = bt.assign_names(["a"])
11.409 +bt.new_branchpoint() # begin
11.410 +bt.new_branch() # if ...
11.411 +ap = bt.use_attribute("a", "p")
11.412 +bt.abandon_branch()
11.413 +bt.shelve_branch() # ... if
11.414 +bt.new_branch() # (null)
11.415 +bt.shelve_branch()
11.416 +bt.merge_branches() # end
11.417 +a2 = bt.assign_names(["a"])
11.418 +bt.new_branchpoint() # begin
11.419 +bt.new_branch() # if ...
11.420 +aq = bt.use_attribute("a", "q")
11.421 +bt.abandon_branch()
11.422 +bt.shelve_branch() # ... if
11.423 +bt.new_branch() # (null)
11.424 +bt.shelve_branch()
11.425 +bt.merge_branches() # end
11.426 +
11.427 +print a1.get_usage() == \
11.428 + {'a' : set([('p',), ()])}, a1.get_usage()
11.429 +print a2.get_usage() == \
11.430 + {'a' : set([('q',), ()])}, a2.get_usage()
11.431 +print bt.get_assignment_positions_for_branches("a", ap) == [0], \
11.432 + bt.get_assignment_positions_for_branches("a", ap)
11.433 +print bt.get_assignment_positions_for_branches("a", aq) == [1], \
11.434 + bt.get_assignment_positions_for_branches("a", aq)
11.435 +names.append(bt.assignments["a"])
11.436 +
11.437 +# Equivalent to...
11.438 +#
11.439 +# a = {}
11.440 +# a.p
11.441 +# if ...:
11.442 +# a = ...
11.443 +# a.x
11.444 +# else:
11.445 +# ...
11.446 +# a.q
11.447 +
11.448 +bt = branching.BranchTracker()
11.449 +a1 = bt.assign_names(["a"], ["<instance>:__builtins__.dict.dict"])
11.450 +ap = bt.use_attribute("a", "p")
11.451 +bt.new_branchpoint() # begin
11.452 +bt.new_branch() # if ...
11.453 +a2 = bt.assign_names(["a"]) # a = ...
11.454 +ax = bt.use_attribute("a", "x")
11.455 +bt.shelve_branch()
11.456 +bt.new_branch() # else
11.457 +bt.shelve_branch()
11.458 +bt.merge_branches() # end
11.459 +aq = bt.use_attribute("a", "q")
11.460 +
11.461 +print a1.get_usage() == \
11.462 + {'a' : set([('p',), ('p', 'q')])}, \
11.463 + a1.get_usage()
11.464 +print a2.get_usage() == \
11.465 + {'a' : set([('q', 'x')])}, \
11.466 + a2.get_usage()
11.467 +print bt.get_assignment_positions_for_branches("a", ap) == [0], \
11.468 + bt.get_assignment_positions_for_branches("a", ap)
11.469 +print bt.get_assignment_positions_for_branches("a", ax) == [1], \
11.470 + bt.get_assignment_positions_for_branches("a", ax)
11.471 +print bt.get_assignment_positions_for_branches("a", aq) == [0, 1], \
11.472 + bt.get_assignment_positions_for_branches("a", aq)
11.473 +names.append(bt.assignments["a"])
11.474 +
11.475 +# Equivalent to...
11.476 +#
11.477 +# if ...:
11.478 +# a = ...
11.479 +# a.x
11.480 +# else:
11.481 +# ...
11.482 +# a.q
11.483 +
11.484 +bt = branching.BranchTracker()
11.485 +bt.new_branchpoint() # begin
11.486 +bt.new_branch() # if ...
11.487 +a1 = bt.assign_names(["a"]) # a = ...
11.488 +ax = bt.use_attribute("a", "x")
11.489 +bt.shelve_branch()
11.490 +bt.new_branch() # else
11.491 +bt.shelve_branch()
11.492 +bt.merge_branches() # end
11.493 +aq = bt.use_attribute("a", "q")
11.494 +
11.495 +print a1.get_usage() == \
11.496 + {'a' : set([('q', 'x')])}, \
11.497 + a1.get_usage()
11.498 +print bt.get_assignment_positions_for_branches("a", aq) == [None, 0], \
11.499 + bt.get_assignment_positions_for_branches("a", aq)
11.500 +names.append(bt.assignments["a"])
11.501 +
11.502 +# Equivalent to...
11.503 +#
11.504 +# if ...:
11.505 +# a = ...
11.506 +# return
11.507 +# a.q
11.508 +
11.509 +bt = branching.BranchTracker()
11.510 +bt.new_branchpoint() # begin
11.511 +bt.new_branch() # if ...
11.512 +a1 = bt.assign_names(["a"])
11.513 +bt.abandon_returning_branch()
11.514 +bt.shelve_branch()
11.515 +bt.new_branch() # (null)
11.516 +bt.shelve_branch()
11.517 +bt.merge_branches() # end
11.518 +aq = bt.use_attribute("a", "q")
11.519 +
11.520 +print a1.get_usage() == \
11.521 + {'a' : set([()])}, a1.get_usage()
11.522 +print bt.get_assignment_positions_for_branches("a", aq) == [None], \
11.523 + bt.get_assignment_positions_for_branches("a", aq)
11.524 +names.append(bt.assignments["a"])
11.525 +
11.526 +# Equivalent to...
11.527 +#
11.528 +# a = ...
11.529 +# try:
11.530 +# if ...:
11.531 +# a.p
11.532 +# return
11.533 +# a.q
11.534 +# finally:
11.535 +# a.r
11.536 +
11.537 +bt = branching.BranchTracker()
11.538 +a1 = bt.assign_names(["a"])
11.539 +bt.new_branchpoint() # begin
11.540 +bt.new_branch() # if ...
11.541 +bt.use_attribute("a", "p")
11.542 +bt.abandon_returning_branch()
11.543 +bt.shelve_branch() # ... if
11.544 +bt.new_branch() # (null)
11.545 +bt.shelve_branch()
11.546 +bt.merge_branches() # end
11.547 +bt.use_attribute("a", "q")
11.548 +branches = bt.resume_all_abandoned_branches()
11.549 +bt.use_attribute("a", "r")
11.550 +bt.restore_active_branches(branches)
11.551 +
11.552 +print a1.get_usage() == \
11.553 + {'a' : set([('p', 'r'), ('q', 'r')])}, a1.get_usage()
11.554 +names.append(bt.assignments["a"])
11.555 +
11.556 +# Equivalent to...
11.557 +#
11.558 +# a = ...
11.559 +# try:
11.560 +# if ...:
11.561 +# a = ...
11.562 +# a.p
11.563 +# return
11.564 +# a.q
11.565 +# finally:
11.566 +# a.r
11.567 +
11.568 +bt = branching.BranchTracker()
11.569 +a1 = bt.assign_names(["a"])
11.570 +bt.new_branchpoint() # begin
11.571 +bt.new_branch() # if ...
11.572 +a2 = bt.assign_names(["a"])
11.573 +bt.use_attribute("a", "p")
11.574 +bt.abandon_returning_branch()
11.575 +bt.shelve_branch() # ... if
11.576 +bt.new_branch() # (null)
11.577 +bt.shelve_branch()
11.578 +bt.merge_branches() # end
11.579 +aq = bt.use_attribute("a", "q")
11.580 +branches = bt.resume_all_abandoned_branches()
11.581 +ar = bt.use_attribute("a", "r")
11.582 +bt.restore_active_branches(branches)
11.583 +
11.584 +print a1.get_usage() == \
11.585 + {'a' : set([(), ('q', 'r')])}, a1.get_usage()
11.586 +print a2.get_usage() == \
11.587 + {'a' : set([('p', 'r')])}, a2.get_usage()
11.588 +print bt.get_assignment_positions_for_branches("a", ar) == [0, 1], \
11.589 + bt.get_assignment_positions_for_branches("a", ar)
11.590 +names.append(bt.assignments["a"])
11.591 +
11.592 +# Equivalent to...
11.593 +#
11.594 +# a = ...
11.595 +# try:
11.596 +# if ...:
11.597 +# a = ...
11.598 +# a.p
11.599 +# return
11.600 +# a.q
11.601 +# except:
11.602 +# a.r
11.603 +
11.604 +bt = branching.BranchTracker()
11.605 +a1 = bt.assign_names(["a"])
11.606 +bt.new_branchpoint() # begin (try)
11.607 +bt.new_branchpoint() # begin
11.608 +bt.new_branch() # if ...
11.609 +a2 = bt.assign_names(["a"])
11.610 +bt.use_attribute("a", "p")
11.611 +bt.abandon_returning_branch()
11.612 +bt.shelve_branch() # ... if
11.613 +bt.new_branch() # (null)
11.614 +bt.shelve_branch()
11.615 +bt.merge_branches() # end
11.616 +bt.use_attribute("a", "q")
11.617 +bt.resume_abandoned_branches() # except
11.618 +ar = bt.use_attribute("a", "r")
11.619 +bt.shelve_branch()
11.620 +bt.merge_branches() # end
11.621 +
11.622 +print a1.get_usage() == \
11.623 + {'a' : set([(), ('q', 'r')])}, a1.get_usage()
11.624 +print a2.get_usage() == \
11.625 + {'a' : set([('p',)])}, a2.get_usage()
11.626 +print bt.get_assignment_positions_for_branches("a", ar) == [0, 1], \
11.627 + bt.get_assignment_positions_for_branches("a", ar)
11.628 +names.append(bt.assignments["a"])
11.629 +
11.630 +# vim: tabstop=4 expandtab shiftwidth=4
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/internal_tests/references.py Tue Aug 30 16:51:10 2016 +0200
12.3 @@ -0,0 +1,87 @@
12.4 +#!/usr/bin/env python
12.5 +
12.6 +from referencing import decode_reference, Reference
12.7 +
12.8 +def show_test(v1, v2):
12.9 + print "%r %r %r" % (v1 == v2, v1, v2)
12.10 +
12.11 +# Compare decoded and constructed references.
12.12 +
12.13 +var1 = decode_reference("<var>")
12.14 +var2 = Reference("<var>")
12.15 +show_test(var1, var2)
12.16 +
12.17 +# Compare with var with superfluous origin.
12.18 +
12.19 +var3 = Reference("<var>", "whatever")
12.20 +show_test(var1, var3)
12.21 +
12.22 +# Compare with var and alias.
12.23 +
12.24 +var4 = Reference("<var>", None, "attribute")
12.25 +show_test(var1, var4)
12.26 +
12.27 +# Compare with var with superfluous origin and alias.
12.28 +
12.29 +var5 = Reference("<var>", "whatever", "attribute")
12.30 +show_test(var1, var5)
12.31 +show_test(var5.get_origin(), None)
12.32 +
12.33 +# Compare vars with different aliases.
12.34 +
12.35 +var6 = Reference("<var>", None, "other")
12.36 +show_test(var4, var6)
12.37 +
12.38 +# Check aliased var.
12.39 +
12.40 +var7 = var1.alias("attribute")
12.41 +show_test(var7, var4)
12.42 +
12.43 +# Check class references, firstly with someclass being identified as a class.
12.44 +
12.45 +cls1 = decode_reference("<class>", "someclass")
12.46 +cls2 = Reference("<class>", "someclass")
12.47 +show_test(cls1, cls2)
12.48 +
12.49 +# Check aliasing of class references.
12.50 +
12.51 +cls3 = cls1.alias("attribute")
12.52 +cls4 = cls2.alias("other")
12.53 +show_test(cls3, cls4)
12.54 +
12.55 +# Check other class references.
12.56 +
12.57 +cls5 = decode_reference("<class>:someclass")
12.58 +cls6 = Reference("<class>", "someclass")
12.59 +show_test(cls5, cls6)
12.60 +
12.61 +# Check aliasing again.
12.62 +
12.63 +cls7 = cls5.alias("attribute")
12.64 +cls8 = cls6.alias("other")
12.65 +show_test(cls7, cls8)
12.66 +
12.67 +# Check instance references. These do not make sense without an origin.
12.68 +
12.69 +inst1 = decode_reference("<instance>:someclass", "whatever")
12.70 +inst2 = Reference("<instance>", "someclass")
12.71 +show_test(inst1, inst2)
12.72 +
12.73 +# Check instantiation.
12.74 +
12.75 +inst3 = cls5.instance_of()
12.76 +show_test(inst1, inst3)
12.77 +
12.78 +# Check modules.
12.79 +
12.80 +mod1 = decode_reference("somemodule")
12.81 +mod2 = Reference("<module>", "somemodule")
12.82 +show_test(mod1, mod2)
12.83 +
12.84 +mod3 = decode_reference("<module>:somemodule")
12.85 +show_test(mod1, mod3)
12.86 +
12.87 +mod4 = decode_reference("<module>", "somemodule")
12.88 +show_test(mod1, mod4)
12.89 +
12.90 +# vim: tabstop=4 expandtab shiftwidth=4
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/lplc Tue Aug 30 16:51:10 2016 +0200
13.3 @@ -0,0 +1,67 @@
13.4 +#!/usr/bin/env python
13.5 +
13.6 +from errors import *
13.7 +from os.path import abspath, exists, join, split
13.8 +from time import time
13.9 +import importer
13.10 +import sys
13.11 +
13.12 +libdirs = [
13.13 + join(split(__file__)[0], "lib"),
13.14 + "/usr/share/lichen/lib"
13.15 + ]
13.16 +
13.17 +def load_module(filename, module_name):
13.18 + for libdir in libdirs:
13.19 + path = join(libdir, filename)
13.20 + if exists(path):
13.21 + return i.load_from_file(path, module_name)
13.22 + return None
13.23 +
13.24 +def stopwatch(activity, now):
13.25 + print >>sys.stderr, "%s took %.2f seconds" % (activity, time() - now)
13.26 + return time()
13.27 +
13.28 +# Main program.
13.29 +
13.30 +if __name__ == "__main__":
13.31 + args = sys.argv[2:]
13.32 + path = libdirs + sys.path[:]
13.33 +
13.34 + filename = abspath(sys.argv[1])
13.35 + path.append(split(filename)[0])
13.36 +
13.37 + verbose = "-v" in args
13.38 + reset = "-r" in args
13.39 +
13.40 + # Load the program.
13.41 +
13.42 + try:
13.43 + start = now = time()
13.44 +
13.45 + i = importer.Importer(path, "_cache", verbose)
13.46 + m = i.initialise(filename, reset)
13.47 + i.finalise()
13.48 +
13.49 + now = stopwatch("Inspection", now)
13.50 +
13.51 + # Report any errors.
13.52 +
13.53 + except ProcessingError, exc:
13.54 + print exc
13.55 + if "-tb" in args:
13.56 + raise
13.57 + elif "-exit" in args:
13.58 + sys.exit(1)
13.59 +
13.60 + except KeyboardInterrupt:
13.61 + if "-exit" in args:
13.62 + sys.exit(2)
13.63 + else:
13.64 + raise
13.65 +
13.66 + else:
13.67 + if "-exit" in args:
13.68 + sys.exit(0)
13.69 +
13.70 +# vim: tabstop=4 expandtab shiftwidth=4
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/modules.py Tue Aug 30 16:51:10 2016 +0200
14.3 @@ -0,0 +1,1229 @@
14.4 +#!/usr/bin/env python
14.5 +
14.6 +"""
14.7 +Module abstractions.
14.8 +
14.9 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,
14.10 + 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
14.11 +
14.12 +This program is free software; you can redistribute it and/or modify it under
14.13 +the terms of the GNU General Public License as published by the Free Software
14.14 +Foundation; either version 3 of the License, or (at your option) any later
14.15 +version.
14.16 +
14.17 +This program is distributed in the hope that it will be useful, but WITHOUT
14.18 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14.19 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14.20 +details.
14.21 +
14.22 +You should have received a copy of the GNU General Public License along with
14.23 +this program. If not, see <http://www.gnu.org/licenses/>.
14.24 +"""
14.25 +
14.26 +from common import *
14.27 +from encoders import decode_modifier_term, encode_modifiers, encode_usage
14.28 +from referencing import decode_reference, Reference
14.29 +import sys
14.30 +
14.31 +class BasicModule(CommonModule):
14.32 +
14.33 + "The basic module information."
14.34 +
14.35 + def __init__(self, name, importer):
14.36 + CommonModule.__init__(self, name, importer)
14.37 +
14.38 + # Import machinery links.
14.39 +
14.40 + self.loaded = False
14.41 +
14.42 + # Module dependencies.
14.43 +
14.44 + self.imported = set()
14.45 + self.imported_hidden = set()
14.46 + self.imported_names = {}
14.47 + self.revealed = set()
14.48 + self.accessing_modules = set()
14.49 +
14.50 + # Global name information.
14.51 +
14.52 + self.objects = {}
14.53 + self.special = {}
14.54 +
14.55 + # Class relationships.
14.56 +
14.57 + self.classes = {}
14.58 +
14.59 + # Attributes.
14.60 +
14.61 + self.class_attrs = {}
14.62 + self.instance_attrs = {}
14.63 + self.instance_attr_constants = {}
14.64 + self.module_attrs = set()
14.65 +
14.66 + # Names used and missing.
14.67 +
14.68 + self.names_used = {}
14.69 + self.names_missing = {}
14.70 + self.name_references = {} # references to globals
14.71 +
14.72 + # Function details.
14.73 +
14.74 + self.function_parameters = {}
14.75 + self.function_defaults = {}
14.76 + self.function_locals = {}
14.77 + self.scope_globals = {}
14.78 +
14.79 + # Invocation details.
14.80 +
14.81 + self.function_targets = {}
14.82 + self.function_arguments = {}
14.83 +
14.84 + # Attribute usage at module and function levels.
14.85 +
14.86 + self.attr_usage = {}
14.87 + self.name_initialisers = {}
14.88 +
14.89 + # General attribute access expressions.
14.90 +
14.91 + self.attr_accesses = {}
14.92 + self.const_accesses = {}
14.93 +
14.94 + # Attribute accessor definition details.
14.95 +
14.96 + self.attr_accessors = {}
14.97 +
14.98 + # Assignment details for accesses.
14.99 +
14.100 + self.attr_access_modifiers = {}
14.101 +
14.102 + # Initialisation-related details.
14.103 +
14.104 + self.initialised_names = {}
14.105 + self.aliased_names = {}
14.106 +
14.107 + def __repr__(self):
14.108 + return "BasicModule(%r, %r)" % (self.name, self.importer)
14.109 +
14.110 + def resolve(self):
14.111 +
14.112 + "Resolve dependencies and complete definitions."
14.113 +
14.114 + self.resolve_class_bases()
14.115 + self.check_special()
14.116 + self.check_names_used()
14.117 + self.resolve_members()
14.118 + self.resolve_initialisers()
14.119 + self.resolve_literals()
14.120 + self.remove_redundant_accessors()
14.121 + self.set_invocation_usage()
14.122 +
14.123 + # Propagate to the importer information needed in subsequent activities.
14.124 +
14.125 + self.propagate()
14.126 +
14.127 + # Derived information methods.
14.128 +
14.129 + def propagate(self):
14.130 +
14.131 + "Finalise and propagate module information."
14.132 +
14.133 + self.propagate_attrs()
14.134 + self.propagate_name_references()
14.135 + self.propagate_attr_accesses()
14.136 + self.propagate_constants()
14.137 +
14.138 + def unpropagate(self):
14.139 +
14.140 + """
14.141 + Retract information from the importer including information about this
14.142 + module derived by the importer.
14.143 + """
14.144 +
14.145 + del self.importer.all_module_attrs[self.name]
14.146 +
14.147 + for name in self.classes.keys():
14.148 + del self.importer.all_class_attrs[name]
14.149 + del self.importer.all_instance_attrs[name]
14.150 + del self.importer.all_combined_attrs[name]
14.151 + del self.importer.all_instance_attr_constants[name]
14.152 +
14.153 + for name, bases in self.classes.items():
14.154 + for base in bases:
14.155 +
14.156 + # Get the identity of the class from the reference.
14.157 +
14.158 + base = base.get_origin()
14.159 +
14.160 + try:
14.161 + self.importer.subclasses[base].remove(name)
14.162 + except (KeyError, ValueError):
14.163 + pass
14.164 +
14.165 + remove_items(self.importer.all_name_references, self.name_references)
14.166 + remove_items(self.importer.all_initialised_names, self.initialised_names)
14.167 + remove_items(self.importer.all_aliased_names, self.aliased_names)
14.168 + remove_items(self.importer.all_attr_accesses, self.attr_accesses)
14.169 + remove_items(self.importer.all_const_accesses, self.const_accesses)
14.170 + remove_items(self.importer.all_attr_access_modifiers, self.attr_access_modifiers)
14.171 + remove_items(self.importer.all_constants, self.constants)
14.172 + remove_items(self.importer.all_constant_values, self.constant_values)
14.173 +
14.174 + # Remove this module's objects from the importer. Objects are
14.175 + # automatically propagated when defined.
14.176 +
14.177 + for name, ref in self.objects.items():
14.178 + if ref.provided_by_module(self.name) or name in self.importer.hidden:
14.179 + if ref.get_kind() != "<module>":
14.180 + del self.importer.objects[name]
14.181 +
14.182 + def resolve_class_bases(self):
14.183 +
14.184 + "Resolve all class bases since some of them may have been deferred."
14.185 +
14.186 + for name, bases in self.classes.items():
14.187 + resolved = []
14.188 + bad = []
14.189 +
14.190 + for base in bases:
14.191 +
14.192 + # Resolve dependencies.
14.193 +
14.194 + if base.has_kind("<depends>"):
14.195 + ref = self.importer.get_object(base.get_origin())
14.196 + else:
14.197 + ref = base
14.198 +
14.199 + # Obtain the origin of the base class reference.
14.200 +
14.201 + if not ref or not ref.has_kind("<class>"):
14.202 + bad.append(base)
14.203 + break
14.204 +
14.205 + resolved.append(ref)
14.206 +
14.207 + if bad:
14.208 + print >>sys.stderr, "Bases of class %s were not classes." % (name, ", ".join(map(str, bad)))
14.209 + else:
14.210 + self.importer.classes[name] = self.classes[name] = resolved
14.211 +
14.212 + def propagate_attrs(self):
14.213 +
14.214 + "Derive attributes from the class and module member details."
14.215 +
14.216 + # Initialise class attribute records for all classes.
14.217 +
14.218 + for name in self.classes.keys():
14.219 + self.importer.all_class_attrs[name] = self.class_attrs[name] = {}
14.220 +
14.221 + # Separate the objects into module and class attributes.
14.222 +
14.223 + for name in self.objects.keys():
14.224 + if "." in name:
14.225 + parent, attrname = name.rsplit(".", 1)
14.226 + if self.classes.has_key(parent):
14.227 + self.class_attrs[parent][attrname] = name
14.228 + elif parent == self.name:
14.229 + self.module_attrs.add(attrname)
14.230 +
14.231 + # Propagate the module attributes.
14.232 +
14.233 + self.importer.all_module_attrs[self.name] = self.module_attrs
14.234 +
14.235 + def propagate_name_references(self):
14.236 +
14.237 + "Propagate name references for the module."
14.238 +
14.239 + self.importer.all_name_references.update(self.name_references)
14.240 + self.importer.all_initialised_names.update(self.initialised_names)
14.241 + self.importer.all_aliased_names.update(self.aliased_names)
14.242 +
14.243 + def propagate_attr_accesses(self):
14.244 +
14.245 + "Propagate attribute accesses for the module."
14.246 +
14.247 + self.importer.all_attr_accesses.update(self.attr_accesses)
14.248 + self.importer.all_const_accesses.update(self.const_accesses)
14.249 + self.importer.all_attr_access_modifiers.update(self.attr_access_modifiers)
14.250 +
14.251 + def propagate_constants(self):
14.252 +
14.253 + "Propagate constant values and aliases for the module."
14.254 +
14.255 + self.importer.all_constants.update(self.constants)
14.256 + self.importer.all_constant_values.update(self.constant_values)
14.257 +
14.258 + for name in self.classes.keys():
14.259 + self.importer.all_instance_attrs[name] = self.instance_attrs.get(name) or {}
14.260 + self.importer.all_instance_attr_constants[name] = self.instance_attr_constants.get(name) or {}
14.261 +
14.262 + # Module-relative naming.
14.263 +
14.264 + def is_global(self, name):
14.265 +
14.266 + """
14.267 + Return whether 'name' is registered as a global in the current
14.268 + namespace.
14.269 + """
14.270 +
14.271 + path = self.get_namespace_path()
14.272 + return name in self.scope_globals.get(path, [])
14.273 +
14.274 + def get_globals(self):
14.275 +
14.276 + """
14.277 + Get the globals from this module, returning a dictionary mapping names
14.278 + to references incorporating original definition details.
14.279 + """
14.280 +
14.281 + l = []
14.282 + for name, value in self.objects.items():
14.283 + parent, attrname = name.rsplit(".", 1)
14.284 + if parent == self.name:
14.285 + l.append((attrname, value))
14.286 + return dict(l)
14.287 +
14.288 + def get_global(self, name):
14.289 +
14.290 + """
14.291 + Get the global of the given 'name' from this module, returning a
14.292 + reference incorporating the original definition details.
14.293 + """
14.294 +
14.295 + path = self.get_global_path(name)
14.296 + return self.objects.get(path)
14.297 +
14.298 + # Name definition discovery.
14.299 +
14.300 + def get_global_or_builtin(self, name):
14.301 +
14.302 + """
14.303 + Return the object recorded the given 'name' from this module or from the
14.304 + __builtins__ module. If no object has yet been defined, perhaps due to
14.305 + circular module references, None is returned.
14.306 + """
14.307 +
14.308 + return self.get_global(name) or self.get_builtin(name)
14.309 +
14.310 + def get_builtin(self, name):
14.311 +
14.312 + "Return the object providing the given built-in 'name'."
14.313 +
14.314 + path = "__builtins__.%s" % name
14.315 +
14.316 + # Obtain __builtins__.
14.317 +
14.318 + module = self.ensure_builtins(path)
14.319 +
14.320 + # Attempt to find the named object within __builtins__.
14.321 +
14.322 + if module:
14.323 + self.find_imported_name(name, module.name, module)
14.324 +
14.325 + # Return the path and any reference for the named object.
14.326 +
14.327 + return self.importer.get_object(path)
14.328 +
14.329 + def ensure_builtins(self, path):
14.330 +
14.331 + """
14.332 + If 'path' is a reference to an object within __builtins__, return the
14.333 + __builtins__ module.
14.334 + """
14.335 +
14.336 + if path.split(".", 1)[0] == "__builtins__":
14.337 + return self.importer.load("__builtins__", True, True)
14.338 + else:
14.339 + return None
14.340 +
14.341 + def get_object(self, path):
14.342 +
14.343 + """
14.344 + Get the details of an object with the given 'path', either from this
14.345 + module or from the whole program. Return a tuple containing the path and
14.346 + any object found.
14.347 + """
14.348 +
14.349 + if self.objects.has_key(path):
14.350 + return self.objects[path]
14.351 + else:
14.352 + self.ensure_builtins(path)
14.353 + return self.importer.get_object(path)
14.354 +
14.355 + def set_object(self, name, value=None):
14.356 +
14.357 + "Set an object with the given 'name' and the given 'value'."
14.358 +
14.359 + ref = decode_reference(value, name)
14.360 + multiple = self.objects.has_key(name) and self.objects[name].get_kind() != ref.get_kind()
14.361 + self.importer.objects[name] = self.objects[name] = multiple and ref.as_var() or ref
14.362 +
14.363 + # Special names.
14.364 +
14.365 + def get_special(self, name):
14.366 +
14.367 + "Return any stored value for the given special 'name'."
14.368 +
14.369 + return self.special.get(name)
14.370 +
14.371 + def set_special(self, name, value):
14.372 +
14.373 + """
14.374 + Set a special 'name' that merely tracks the use of an implicit object
14.375 + 'value'.
14.376 + """
14.377 +
14.378 + self.special[name] = value
14.379 +
14.380 + def set_special_literal(self, name, ref):
14.381 +
14.382 + """
14.383 + Set a special name for the literal type 'name' having type 'ref'. Such
14.384 + special names provide a way of referring to literal object types.
14.385 + """
14.386 +
14.387 + literal_name = "$L%s" % name
14.388 + value = ResolvedNameRef(literal_name, ref)
14.389 + self.set_special(literal_name, value)
14.390 +
14.391 + # Revealing modules by tracking name imports across modules.
14.392 +
14.393 + def set_imported_name(self, name, module_name, alias=None, path=None):
14.394 +
14.395 + """
14.396 + Record 'name' as being imported from the given 'module_name', employing
14.397 + the given 'alias' in the local namespace if specified.
14.398 + """
14.399 +
14.400 + path = path or self.get_namespace_path()
14.401 + init_item(self.imported_names, path, dict)
14.402 + self.imported_names[path][alias or name] = (name, module_name)
14.403 +
14.404 + def get_imported_name(self, name, path):
14.405 +
14.406 + "Get details of any imported 'name' within the namespace at 'path'."
14.407 +
14.408 + if self.imported_names.has_key(path):
14.409 + return self.imported_names[path].get(name)
14.410 + else:
14.411 + return None
14.412 +
14.413 + def find_imported_name(self, name, path, module=None):
14.414 +
14.415 + """
14.416 + Find details of the imported 'name' within the namespace at 'path',
14.417 + starting within the given 'module' if indicated, or within this module
14.418 + otherwise.
14.419 + """
14.420 +
14.421 + module = module or self
14.422 +
14.423 + # Obtain any module required by the name.
14.424 +
14.425 + name_modname = module.get_imported_name(name, path)
14.426 +
14.427 + if name_modname:
14.428 + name, modname = name_modname
14.429 + module = self._find_imported_name(name, modname, module)
14.430 +
14.431 + # Obtain the name from the final module, revealing it if appropriate.
14.432 +
14.433 + if module:
14.434 + init_item(self.importer.revealing, module.name, set)
14.435 + self.importer.set_revealing(module, name, self)
14.436 +
14.437 + def _find_imported_name(self, name, modname, module):
14.438 +
14.439 + """
14.440 + Traverse details for 'name' via 'modname' to the module providing the
14.441 + name, tentatively revealing the module even if the module is not yet
14.442 + loaded and cannot provide the details of the object recorded for the
14.443 + name.
14.444 + """
14.445 +
14.446 + _name = name
14.447 +
14.448 + # Obtain any modules referenced by each required module.
14.449 +
14.450 + while True:
14.451 +
14.452 + # Get the module directly or traverse possibly-aliased names.
14.453 +
14.454 + module = self.get_module_direct(modname, True)
14.455 + if not module:
14.456 + top, module = self.get_module(modname, True)
14.457 + name_modname = module.get_imported_name(_name, module.name)
14.458 + if not name_modname:
14.459 + break
14.460 + else:
14.461 + _name, modname = name_modname
14.462 +
14.463 + return module
14.464 +
14.465 + def reveal_referenced(self):
14.466 +
14.467 + """
14.468 + Reveal modules referenced by this module.
14.469 + """
14.470 +
14.471 + for path, names in self.imported_names.items():
14.472 + for alias, (name, modname) in names.items():
14.473 + module = self._find_imported_name(name, modname, self)
14.474 + self.reveal_module(module)
14.475 +
14.476 + def reveal_module(self, module):
14.477 +
14.478 + """
14.479 + Reveal the given 'module', recording the revealed modules on this
14.480 + module.
14.481 + """
14.482 +
14.483 + if module is not self:
14.484 + self.importer.reveal_module(module)
14.485 + self.revealed.add(module)
14.486 + module.accessing_modules.add(self.name)
14.487 +
14.488 + # Module loading.
14.489 +
14.490 + def get_module_direct(self, modname, hidden=False):
14.491 +
14.492 + """
14.493 + Return 'modname' without traversing parent modules, keeping the module
14.494 + 'hidden' if set to a true value, loading the module if not already
14.495 + loaded.
14.496 + """
14.497 +
14.498 + return self.importer.get_module(modname, True) or self.importer.load(modname, hidden=hidden)
14.499 +
14.500 + def get_module(self, name, hidden=False):
14.501 +
14.502 + """
14.503 + Use the given 'name' to obtain the identity of a module. Return module
14.504 + objects or None if the module cannot be found. This method is required
14.505 + when aliases are used to refer to modules and where a module "path" does
14.506 + not correspond to the actual module path.
14.507 +
14.508 + A tuple is returned containing the top or base module and the deepest or
14.509 + leaf module involved.
14.510 + """
14.511 +
14.512 + path_so_far = []
14.513 + top = module = None
14.514 + parts = name.split(".")
14.515 +
14.516 + for i, part in enumerate(parts):
14.517 + path_so_far.append(part)
14.518 + module_name = ".".join(path_so_far)
14.519 + ref = self.get_object(module_name)
14.520 +
14.521 + # If no known object exists, attempt to load it.
14.522 +
14.523 + if not ref:
14.524 + module = self.importer.load(module_name, True, hidden)
14.525 + if not module:
14.526 + return None
14.527 +
14.528 + # Rewrite the path to use the actual module details.
14.529 +
14.530 + path_so_far = module.name.split(".")
14.531 +
14.532 + # If the object exists and is not a module, stop.
14.533 +
14.534 + elif ref.has_kind(["<class>", "<function>", "<var>"]):
14.535 + return None
14.536 +
14.537 + else:
14.538 + module = self.importer.get_module(ref.get_origin(), hidden)
14.539 +
14.540 + if not top:
14.541 + top = module
14.542 +
14.543 + return top, module
14.544 +
14.545 +class CachedModule(BasicModule):
14.546 +
14.547 + "A cached module."
14.548 +
14.549 + def __repr__(self):
14.550 + return "CachedModule(%r, %r)" % (self.name, self.importer)
14.551 +
14.552 + def resolve(self):
14.553 + pass
14.554 +
14.555 + def to_cache(self, filename):
14.556 +
14.557 + "Not actually writing the module back to 'filename'."
14.558 +
14.559 + pass
14.560 +
14.561 + def from_cache(self, filename):
14.562 +
14.563 + """
14.564 + Read a module's details from the file with the given 'filename' as
14.565 + described in the to_cache method of InspectedModule.
14.566 + """
14.567 +
14.568 + f = open(filename)
14.569 + try:
14.570 + self.filename = f.readline().rstrip()
14.571 +
14.572 + accessing_modules = f.readline().split(": ", 1)[-1].rstrip()
14.573 +
14.574 + module_names = f.readline().split(": ", 1)[-1].rstrip()
14.575 + if module_names:
14.576 + for module_name in module_names.split(", "):
14.577 + self.imported.add(self.importer.load(module_name))
14.578 +
14.579 + module_names = f.readline().split(": ", 1)[-1].rstrip()
14.580 + if module_names:
14.581 + for module_name in module_names.split(", "):
14.582 + self.imported_hidden.add(self.importer.load(module_name, hidden=True))
14.583 +
14.584 + module_names = f.readline().split(": ", 1)[-1].rstrip()
14.585 + if module_names:
14.586 + for module_name in module_names.split(", "):
14.587 + module = self.importer.load(module_name, True, True)
14.588 + self.reveal_module(module)
14.589 +
14.590 + f.readline() # (empty line)
14.591 +
14.592 + self._get_imported_names(f)
14.593 + self._get_members(f)
14.594 + self._get_class_relationships(f)
14.595 + self._get_instance_attrs(f)
14.596 + self._get_instance_attr_constants(f)
14.597 + self.from_lines(f, self.names_used) # "names used:"
14.598 + self.from_lines(f, self.names_missing) # "names missing:"
14.599 + self._get_name_references(f)
14.600 + self._get_initialised_names(f)
14.601 + self._get_aliased_names(f)
14.602 + self._get_function_parameters(f)
14.603 + self._get_function_defaults(f)
14.604 + self._get_function_locals(f)
14.605 + self.from_lines(f, self.scope_globals) # "scope globals:"
14.606 + self._get_function_targets(f)
14.607 + self._get_function_arguments(f)
14.608 + self._get_attribute_usage(f)
14.609 + self._get_attr_accesses(f)
14.610 + self._get_const_accesses(f)
14.611 + self._get_attr_accessors(f)
14.612 + self._get_attr_access_modifiers(f)
14.613 + self._get_constant_literals(f)
14.614 + self._get_constant_values(f)
14.615 +
14.616 + finally:
14.617 + f.close()
14.618 +
14.619 + self.loaded = True
14.620 +
14.621 + def resolve(self):
14.622 + self.propagate()
14.623 +
14.624 + def _get_imported_names(self, f):
14.625 + f.readline() # "imported names:"
14.626 + line = f.readline().rstrip()
14.627 + while line:
14.628 + path, alias_or_name, name, module_name = self._get_fields(line, 4)
14.629 + init_item(self.imported_names, path, dict)
14.630 + self.imported_names[path][alias_or_name] = (name, module_name)
14.631 + line = f.readline().rstrip()
14.632 +
14.633 + def _get_members(self, f):
14.634 + f.readline() # "members:"
14.635 + line = f.readline().rstrip()
14.636 + while line:
14.637 + name, ref = line.split(" ", 1)
14.638 + self.set_object(name, ref)
14.639 + line = f.readline().rstrip()
14.640 +
14.641 + def _get_class_relationships(self, f):
14.642 + f.readline() # "class relationships:"
14.643 + line = f.readline().rstrip()
14.644 + while line:
14.645 + name, value = self._get_fields(line)
14.646 + values = value and value.split(", ") or []
14.647 + self.importer.classes[name] = self.classes[name] = map(decode_reference, values)
14.648 + self.importer.subclasses[name] = set()
14.649 + line = f.readline().rstrip()
14.650 +
14.651 + def _get_instance_attrs(self, f):
14.652 + f.readline() # "instance attributes:"
14.653 + line = f.readline().rstrip()
14.654 + while line:
14.655 + name, value = self._get_fields(line)
14.656 + self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or [])
14.657 + line = f.readline().rstrip()
14.658 +
14.659 + def _get_instance_attr_constants(self, f):
14.660 + f.readline() # "instance attribute constants:"
14.661 + line = f.readline().rstrip()
14.662 + while line:
14.663 + name, attrname, ref = self._get_fields(line, 3)
14.664 + init_item(self.instance_attr_constants, name, dict)
14.665 + self.instance_attr_constants[name][attrname] = decode_reference(ref)
14.666 + line = f.readline().rstrip()
14.667 +
14.668 + def _get_name_references(self, f):
14.669 + f.readline() # "name references:"
14.670 + line = f.readline().rstrip()
14.671 + while line:
14.672 + name, value = self._get_fields(line)
14.673 + self.name_references[name] = value
14.674 + line = f.readline().rstrip()
14.675 +
14.676 + def _get_initialised_names(self, f):
14.677 + f.readline() # "initialised names:"
14.678 + line = f.readline().rstrip()
14.679 + while line:
14.680 + name, version, value = self._get_fields(line, 3)
14.681 + init_item(self.initialised_names, name, dict)
14.682 + self.initialised_names[name][int(version)] = decode_reference(value)
14.683 + line = f.readline().rstrip()
14.684 +
14.685 + def _get_aliased_names(self, f):
14.686 + f.readline() # "aliased names:"
14.687 + line = f.readline().rstrip()
14.688 + while line:
14.689 + name, version, original_name, attrnames, number = self._get_fields(line, 5)
14.690 + init_item(self.aliased_names, name, dict)
14.691 + if number == "{}": number = None
14.692 + else: number = int(number)
14.693 + self.aliased_names[name][int(version)] = (original_name, attrnames != "{}" and attrnames or None, number)
14.694 + line = f.readline().rstrip()
14.695 +
14.696 + def _get_function_parameters(self, f):
14.697 + f.readline() # "function parameters:"
14.698 + line = f.readline().rstrip()
14.699 + while line:
14.700 + function, names = self._get_fields(line)
14.701 + self.importer.function_parameters[function] = \
14.702 + self.function_parameters[function] = names and names.split(", ") or []
14.703 + line = f.readline().rstrip()
14.704 +
14.705 + def _get_function_defaults(self, f):
14.706 + f.readline() # "function default parameters:"
14.707 + line = f.readline().rstrip()
14.708 + while line:
14.709 + function, defaults = self._get_fields(line)
14.710 + self.importer.function_defaults[function] = \
14.711 + self.function_defaults[function] = l = []
14.712 + if defaults != "{}":
14.713 + for value in defaults.split(", "):
14.714 + name, default = value.split("=")
14.715 + default = decode_reference(default)
14.716 + l.append((name, default))
14.717 + line = f.readline().rstrip()
14.718 +
14.719 + def _get_function_locals(self, f):
14.720 + f.readline() # "function locals:"
14.721 + line = f.readline().rstrip()
14.722 + while line:
14.723 + function, name, value = self._get_fields(line, 3)
14.724 + init_item(self.function_locals, function, dict)
14.725 + if name != "{}":
14.726 + self.function_locals[function][name] = decode_reference(value)
14.727 + line = f.readline().rstrip()
14.728 +
14.729 + def _get_function_targets(self, f):
14.730 + f.readline() # "function targets:"
14.731 + line = f.readline().rstrip()
14.732 + while line:
14.733 + function, n = self._get_fields(line)
14.734 + self.importer.function_targets[function] = \
14.735 + self.function_targets[function] = int(n)
14.736 + line = f.readline().rstrip()
14.737 +
14.738 + def _get_function_arguments(self, f):
14.739 + f.readline() # "function arguments:"
14.740 + line = f.readline().rstrip()
14.741 + while line:
14.742 + function, n = self._get_fields(line)
14.743 + self.importer.function_arguments[function] = \
14.744 + self.function_arguments[function] = int(n)
14.745 + line = f.readline().rstrip()
14.746 +
14.747 + def _get_attribute_usage(self, f):
14.748 + f.readline() # "attribute usage:"
14.749 + line = f.readline().rstrip()
14.750 + while line:
14.751 + unit, value = self._get_fields(line)
14.752 + init_item(self.attr_usage, unit, dict)
14.753 + self.usage_from_cache(value, self.attr_usage[unit])
14.754 + line = f.readline().rstrip()
14.755 +
14.756 + def _get_attr_accesses(self, f):
14.757 + f.readline() # "attribute accesses:"
14.758 + line = f.readline().rstrip()
14.759 + while line:
14.760 + name, value = self._get_fields(line)
14.761 + self.attr_accesses[name] = set(value.split(", "))
14.762 + line = f.readline().rstrip()
14.763 +
14.764 + def _get_const_accesses(self, f):
14.765 + f.readline() # "constant accesses:"
14.766 + line = f.readline().rstrip()
14.767 + while line:
14.768 + name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6)
14.769 + if attrnames == "{}": attrnames = None
14.770 + init_item(self.const_accesses, name, dict)
14.771 + self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "")
14.772 + line = f.readline().rstrip()
14.773 +
14.774 + def _get_attr_accessors(self, f):
14.775 + f.readline() # "attribute access usage:"
14.776 + line = f.readline().rstrip()
14.777 + while line:
14.778 + objpath, name, attrname, value = self._get_fields(line, 4)
14.779 + if attrname == "{}": attrname = None
14.780 + access = name, attrname
14.781 + init_item(self.attr_accessors, objpath, dict)
14.782 + init_item(self.attr_accessors[objpath], access, list)
14.783 + positions = map(int, value.split(", "))
14.784 + self.attr_accessors[objpath][access].append(positions)
14.785 + line = f.readline().rstrip()
14.786 +
14.787 + def _get_attr_access_modifiers(self, f):
14.788 + f.readline() # "attribute access modifiers:"
14.789 + line = f.readline().rstrip()
14.790 + while line:
14.791 + objpath, name, attrnames, value = self._get_fields(line, 4)
14.792 + if name == "{}": name = None
14.793 + if attrnames == "{}": attrnames = None
14.794 + access = name, attrnames
14.795 + init_item(self.attr_access_modifiers, objpath, dict)
14.796 + init_item(self.attr_access_modifiers[objpath], access, list)
14.797 + modifiers = [decode_modifier_term(s) for s in value]
14.798 + self.attr_access_modifiers[objpath][access] = modifiers
14.799 + line = f.readline().rstrip()
14.800 +
14.801 + def _get_constant_literals(self, f):
14.802 + f.readline() # "constant literals:"
14.803 + line = f.readline().rstrip()
14.804 + last_path = None
14.805 + n = None
14.806 + while line:
14.807 + path, constant = self._get_fields(line)
14.808 + if path != last_path:
14.809 + n = 0
14.810 + last_path = path
14.811 + else:
14.812 + n += 1
14.813 + init_item(self.constants, path, dict)
14.814 + self.constants[path][eval(constant)] = n
14.815 + line = f.readline().rstrip()
14.816 +
14.817 + def _get_constant_values(self, f):
14.818 + f.readline() # "constant values:"
14.819 + line = f.readline().rstrip()
14.820 + while line:
14.821 + name, value_type, value = self._get_fields(line, 3)
14.822 + self.constant_values[name] = eval(value), value_type
14.823 + line = f.readline().rstrip()
14.824 +
14.825 + # Generic parsing methods.
14.826 +
14.827 + def from_lines(self, f, d):
14.828 +
14.829 + "Read lines from 'f', populating 'd'."
14.830 +
14.831 + f.readline() # section heading
14.832 + line = f.readline().rstrip()
14.833 + while line:
14.834 + name, value = self._get_fields(line)
14.835 + d[name] = set(value and value.split(", ") or [])
14.836 + line = f.readline().rstrip()
14.837 +
14.838 + def usage_from_cache(self, value, mapping):
14.839 +
14.840 + """
14.841 + Interpret the given 'value' containing name and usage information,
14.842 + storing the information in the given 'mapping'.
14.843 + """
14.844 +
14.845 + local, usage = self._get_fields(value)
14.846 + init_item(mapping, local, list)
14.847 + self._usage_from_cache(mapping[local], usage)
14.848 +
14.849 + def _usage_from_cache(self, d, usage):
14.850 +
14.851 + # Interpret descriptions of each version of the name.
14.852 +
14.853 + all_usages = set()
14.854 + for attrnames in usage.split("; "):
14.855 + if attrnames == "{}":
14.856 + all_attrnames = ()
14.857 + else:
14.858 + # Decode attribute details for each usage description.
14.859 +
14.860 + all_attrnames = set()
14.861 + for attrname_str in attrnames.split(", "):
14.862 + all_attrnames.add(attrname_str)
14.863 +
14.864 + all_attrnames = list(all_attrnames)
14.865 + all_attrnames.sort()
14.866 +
14.867 + all_usages.add(tuple(all_attrnames))
14.868 +
14.869 + d.append(all_usages)
14.870 +
14.871 + def _get_fields(self, s, n=2):
14.872 + result = s.split(" ", n-1)
14.873 + if len(result) == n:
14.874 + return result
14.875 + else:
14.876 + return tuple(result) + tuple([""] * (n - len(result)))
14.877 +
14.878 +class CacheWritingModule:
14.879 +
14.880 + """
14.881 + A mix-in providing cache-writing support, to be combined with BasicModule.
14.882 + """
14.883 +
14.884 + def to_cache(self, filename):
14.885 +
14.886 + """
14.887 + Write a cached representation of the inspected module with the following
14.888 + format to the file having the given 'filename':
14.889 +
14.890 + filename
14.891 + "accessed by:" accessing module names during this module's import
14.892 + "imports:" imported module names
14.893 + "hidden imports:" imported module names
14.894 + "reveals:" revealed module names
14.895 + (empty line)
14.896 + "imported names:"
14.897 + zero or more: qualified name " " name " " module name
14.898 + (empty line)
14.899 + "members:"
14.900 + zero or more: qualified name " " reference
14.901 + (empty line)
14.902 + "class relationships:"
14.903 + zero or more: qualified class name " " base class references
14.904 + (empty line)
14.905 + "instance attributes:"
14.906 + zero or more: qualified class name " " instance attribute names
14.907 + (empty line)
14.908 + "instance attribute constants:"
14.909 + zero or more: qualified class name " " attribute name " " reference
14.910 + (empty line)
14.911 + "names used:"
14.912 + zero or more: qualified class/function/module name " " names
14.913 + (empty line)
14.914 + "names missing:"
14.915 + zero or more: qualified class/function/module name " " names
14.916 + (empty line)
14.917 + "name references:"
14.918 + zero or more: qualified name " " reference
14.919 + (empty line)
14.920 + "initialised names:"
14.921 + zero or more: qualified name " " definition version " " reference
14.922 + (empty line)
14.923 + "aliased names:"
14.924 + zero or more: qualified name " " definition version " " original name " " attribute names " " access number
14.925 + (empty line)
14.926 + "function parameters:"
14.927 + zero or more: qualified function name " " parameter names
14.928 + (empty line)
14.929 + "function default parameters:"
14.930 + zero or more: qualified function name " " parameter names with defaults
14.931 + (empty line)
14.932 + "function locals:"
14.933 + zero or more: qualified function name " " local variable name " " reference
14.934 + (empty line)
14.935 + "scope globals:"
14.936 + zero or more: qualified function name " " global variable names
14.937 + (empty line)
14.938 + "function targets:"
14.939 + zero or more: qualified function name " " maximum number of targets allocated
14.940 + (empty line)
14.941 + "function arguments:"
14.942 + zero or more: qualified function name " " maximum number of arguments allocated
14.943 + (empty line)
14.944 + "attribute usage:"
14.945 + zero or more: qualified scope name " " local/global/qualified variable name " " usages
14.946 + (empty line)
14.947 + "attribute accesses:"
14.948 + zero or more: qualified scope name " " attribute-chains
14.949 + (empty line)
14.950 + "constant accesses:"
14.951 + zero or more: qualified function name " " attribute-chain " " reference " " remaining attribute-chain
14.952 + (empty line)
14.953 + "attribute access usage:"
14.954 + zero or more: qualified function name " " local/global variable name " " attribute name " " definition versions
14.955 + (empty line)
14.956 + "attribute access modifiers:"
14.957 + zero or more: qualified function name " " local/global variable name " " attribute name " " access modifiers
14.958 + "constant literals:"
14.959 + zero or more: qualified scope name " " constant literal
14.960 + "constant values:"
14.961 + zero or more: qualified name " " value type " " constant literal
14.962 +
14.963 + All collections of names are separated by ", " characters.
14.964 +
14.965 + References can be "<var>", a module name, or one of "<class>" or
14.966 + "<function>" followed optionally by a ":" character and a qualified
14.967 + name.
14.968 +
14.969 + Parameter names with defaults are separated by ", " characters, with
14.970 + each name followed by "=" and then followed by a reference. If "{}" is
14.971 + indicated, no defaults are defined for the function. Similarly, function
14.972 + locals may be indicated as "{}" meaning that there are no locals.
14.973 +
14.974 + All usages (attribute usage sets) are separated by "; " characters, with
14.975 + the special string "{}" representing an empty set.
14.976 +
14.977 + Each usage is a collection of names separated by ", " characters, with
14.978 + assigned attribute names suffixed with a "*" character.
14.979 +
14.980 + Each attribute-chain expression is a dot-separated chain of attribute
14.981 + names, with assignments suffixed with a "*" character.
14.982 +
14.983 + Definition versions are separated by ", " characters and indicate the
14.984 + name definition version associated with the access.
14.985 +
14.986 + Access modifiers are separated by ", " characters and indicate features
14.987 + of each access, with multiple accesses described on a single line.
14.988 + """
14.989 +
14.990 + f = open(filename, "w")
14.991 + try:
14.992 + print >>f, self.filename
14.993 +
14.994 + accessing_modules = list(self.accessing_modules)
14.995 + accessing_modules.sort()
14.996 + print >>f, "accessed by:", ", ".join(accessing_modules)
14.997 +
14.998 + module_names = [m.name for m in self.imported]
14.999 + module_names.sort()
14.1000 + print >>f, "imports:", ", ".join(module_names)
14.1001 +
14.1002 + module_names = [m.name for m in self.imported_hidden]
14.1003 + module_names.sort()
14.1004 + print >>f, "hidden imports:", ", ".join(module_names)
14.1005 +
14.1006 + module_names = [m.name for m in self.revealed]
14.1007 + module_names.sort()
14.1008 + print >>f, "reveals:", ", ".join(module_names)
14.1009 +
14.1010 + print >>f
14.1011 + print >>f, "imported names:"
14.1012 + paths = self.imported_names.keys()
14.1013 + paths.sort()
14.1014 + for path in paths:
14.1015 + names = self.imported_names[path].keys()
14.1016 + names.sort()
14.1017 + for alias_or_name in names:
14.1018 + name, modname = self.imported_names[path][alias_or_name]
14.1019 + print >>f, path, alias_or_name, name, modname
14.1020 +
14.1021 + print >>f
14.1022 + print >>f, "members:"
14.1023 + objects = self.objects.keys()
14.1024 + objects.sort()
14.1025 + for name in objects:
14.1026 + print >>f, name, self.objects[name]
14.1027 +
14.1028 + print >>f
14.1029 + print >>f, "class relationships:"
14.1030 + classes = self.classes.keys()
14.1031 + classes.sort()
14.1032 + for class_ in classes:
14.1033 + bases = self.classes[class_]
14.1034 + if bases:
14.1035 + print >>f, class_, ", ".join(map(str, bases))
14.1036 + else:
14.1037 + print >>f, class_
14.1038 +
14.1039 + self.to_lines(f, "instance attributes:", self.instance_attrs)
14.1040 +
14.1041 + print >>f
14.1042 + print >>f, "instance attribute constants:"
14.1043 + classes = self.instance_attr_constants.items()
14.1044 + classes.sort()
14.1045 + for name, attrs in classes:
14.1046 + attrs = attrs.items()
14.1047 + attrs.sort()
14.1048 + for attrname, ref in attrs:
14.1049 + print >>f, name, attrname, ref
14.1050 +
14.1051 + self.to_lines(f, "names used:", self.names_used)
14.1052 + self.to_lines(f, "names missing:", self.names_missing)
14.1053 +
14.1054 + print >>f
14.1055 + print >>f, "name references:"
14.1056 + refs = self.name_references.items()
14.1057 + refs.sort()
14.1058 + for name, ref in refs:
14.1059 + print >>f, name, ref
14.1060 +
14.1061 + print >>f
14.1062 + print >>f, "initialised names:"
14.1063 + assignments = self.initialised_names.items()
14.1064 + assignments.sort()
14.1065 + for name, refs in assignments:
14.1066 + versions = refs.items()
14.1067 + versions.sort()
14.1068 + for version, ref in versions:
14.1069 + print >>f, name, version, ref
14.1070 +
14.1071 + print >>f
14.1072 + print >>f, "aliased names:"
14.1073 + assignments = self.aliased_names.items()
14.1074 + assignments.sort()
14.1075 + for name, aliases in assignments:
14.1076 + versions = aliases.items()
14.1077 + versions.sort()
14.1078 + for version, alias in versions:
14.1079 + original_name, attrnames, number = alias
14.1080 + print >>f, name, version, original_name, attrnames or "{}", number is None and "{}" or number
14.1081 +
14.1082 + print >>f
14.1083 + print >>f, "function parameters:"
14.1084 + functions = self.function_parameters.keys()
14.1085 + functions.sort()
14.1086 + for function in functions:
14.1087 + print >>f, function, ", ".join(self.function_parameters[function])
14.1088 +
14.1089 + print >>f
14.1090 + print >>f, "function default parameters:"
14.1091 + functions = self.function_defaults.keys()
14.1092 + functions.sort()
14.1093 + for function in functions:
14.1094 + parameters = self.function_defaults[function]
14.1095 + if parameters:
14.1096 + print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters])
14.1097 + else:
14.1098 + print >>f, function, "{}"
14.1099 +
14.1100 + print >>f
14.1101 + print >>f, "function locals:"
14.1102 + functions = self.function_locals.keys()
14.1103 + functions.sort()
14.1104 + for function in functions:
14.1105 + names = self.function_locals[function].items()
14.1106 + if names:
14.1107 + names.sort()
14.1108 + for name, value in names:
14.1109 + print >>f, function, name, value
14.1110 + else:
14.1111 + print >>f, function, "{}"
14.1112 +
14.1113 + self.to_lines(f, "scope globals:", self.scope_globals)
14.1114 +
14.1115 + print >>f
14.1116 + print >>f, "function targets:"
14.1117 + functions = self.function_targets.keys()
14.1118 + functions.sort()
14.1119 + for function in functions:
14.1120 + print >>f, function, self.function_targets[function]
14.1121 +
14.1122 + print >>f
14.1123 + print >>f, "function arguments:"
14.1124 + functions = self.function_arguments.keys()
14.1125 + functions.sort()
14.1126 + for function in functions:
14.1127 + print >>f, function, self.function_arguments[function]
14.1128 +
14.1129 + print >>f
14.1130 + print >>f, "attribute usage:"
14.1131 + units = self.attr_usage.keys()
14.1132 + units.sort()
14.1133 + for unit in units:
14.1134 + d = self.attr_usage[unit]
14.1135 + self.usage_to_cache(d, f, unit)
14.1136 +
14.1137 + print >>f
14.1138 + print >>f, "attribute accesses:"
14.1139 + paths = self.attr_accesses.keys()
14.1140 + paths.sort()
14.1141 + for path in paths:
14.1142 + accesses = list(self.attr_accesses[path])
14.1143 + accesses.sort()
14.1144 + print >>f, path, ", ".join(accesses)
14.1145 +
14.1146 + print >>f
14.1147 + print >>f, "constant accesses:"
14.1148 + paths = self.const_accesses.keys()
14.1149 + paths.sort()
14.1150 + for path in paths:
14.1151 + accesses = self.const_accesses[path].items()
14.1152 + accesses.sort()
14.1153 + for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses:
14.1154 + print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}"
14.1155 +
14.1156 + print >>f
14.1157 + print >>f, "attribute access usage:"
14.1158 + paths = self.attr_accessors.keys()
14.1159 + paths.sort()
14.1160 + for path in paths:
14.1161 + all_accesses = self.attr_accessors[path].items()
14.1162 + all_accesses.sort()
14.1163 + for (name, attrname), accesses in all_accesses:
14.1164 + for positions in accesses:
14.1165 + positions = map(str, positions)
14.1166 + print >>f, path, name, attrname or "{}", ", ".join(positions)
14.1167 +
14.1168 + print >>f
14.1169 + print >>f, "attribute access modifiers:"
14.1170 + paths = self.attr_access_modifiers.keys()
14.1171 + paths.sort()
14.1172 + for path in paths:
14.1173 + all_accesses = self.attr_access_modifiers[path].items()
14.1174 + all_accesses.sort()
14.1175 + for (name, attrnames), modifiers in all_accesses:
14.1176 + print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers)
14.1177 +
14.1178 + print >>f
14.1179 + print >>f, "constant literals:"
14.1180 + paths = self.constants.keys()
14.1181 + paths.sort()
14.1182 + for path in paths:
14.1183 + constants = [(v, k) for (k, v) in self.constants[path].items()]
14.1184 + constants.sort()
14.1185 + for n, constant in constants:
14.1186 + print >>f, path, repr(constant)
14.1187 +
14.1188 + print >>f
14.1189 + print >>f, "constant values:"
14.1190 + names = self.constant_values.keys()
14.1191 + names.sort()
14.1192 + for name in names:
14.1193 + value, value_type = self.constant_values[name]
14.1194 + print >>f, name, value_type, repr(value)
14.1195 +
14.1196 + finally:
14.1197 + f.close()
14.1198 +
14.1199 + def to_lines(self, f, heading, d):
14.1200 +
14.1201 + "Write lines to 'f' with the given 'heading', using 'd'."
14.1202 +
14.1203 + print >>f
14.1204 + print >>f, heading
14.1205 + keys = d.keys()
14.1206 + keys.sort()
14.1207 + for key in keys:
14.1208 + attrs = list(d[key])
14.1209 + if attrs:
14.1210 + attrs.sort()
14.1211 + print >>f, key, ", ".join(attrs)
14.1212 +
14.1213 + def usage_to_cache(self, details, f, prefix):
14.1214 +
14.1215 + "Write the given namespace usage details to the cache."
14.1216 +
14.1217 + names = list(details.keys())
14.1218 + if names:
14.1219 + names.sort()
14.1220 + for name in names:
14.1221 + if details[name]:
14.1222 +
14.1223 + # Produce descriptions for each version of the name.
14.1224 +
14.1225 + for version in details[name]:
14.1226 + all_usages = []
14.1227 + for usage in version:
14.1228 + all_usages.append(encode_usage(usage))
14.1229 +
14.1230 + print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages))
14.1231 +
14.1232 +# vim: tabstop=4 expandtab shiftwidth=4
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
15.2 +++ b/referencing.py Tue Aug 30 16:51:10 2016 +0200
15.3 @@ -0,0 +1,175 @@
15.4 +#!/usr/bin/env python
15.5 +
15.6 +"""
15.7 +Reference abstractions.
15.8 +
15.9 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
15.10 +
15.11 +This program is free software; you can redistribute it and/or modify it under
15.12 +the terms of the GNU General Public License as published by the Free Software
15.13 +Foundation; either version 3 of the License, or (at your option) any later
15.14 +version.
15.15 +
15.16 +This program is distributed in the hope that it will be useful, but WITHOUT
15.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15.19 +details.
15.20 +
15.21 +You should have received a copy of the GNU General Public License along with
15.22 +this program. If not, see <http://www.gnu.org/licenses/>.
15.23 +"""
15.24 +
15.25 +class Reference:
15.26 +
15.27 + "A reference abstraction."
15.28 +
15.29 + def __init__(self, kind, origin=None, name=None):
15.30 +
15.31 + """
15.32 + Initialise a reference using 'kind' to indicate the kind of object,
15.33 + 'origin' to indicate the actual origin of a referenced object, and a
15.34 + 'name' indicating an alias for the object in the program structure.
15.35 + """
15.36 +
15.37 + if isinstance(kind, Reference):
15.38 + raise ValueError, (kind, origin)
15.39 + self.kind = kind
15.40 + self.origin = origin
15.41 + self.name = name
15.42 +
15.43 + def __repr__(self):
15.44 + return "Reference(%r, %r, %r)" % (self.kind, self.origin, self.name)
15.45 +
15.46 + def __str__(self):
15.47 +
15.48 + """
15.49 + Serialise the reference as '<var>' or a description incorporating the
15.50 + kind and origin.
15.51 + """
15.52 +
15.53 + if self.kind == "<var>":
15.54 + return self.kind
15.55 + else:
15.56 + return "%s:%s" % (self.kind, self.origin)
15.57 +
15.58 + def __hash__(self):
15.59 +
15.60 + "Hash instances using the kind and origin only."
15.61 +
15.62 + return hash((self.kind, self.get_origin()))
15.63 +
15.64 + def __cmp__(self, other):
15.65 +
15.66 + "Compare with 'other' using the kind and origin only."
15.67 +
15.68 + if isinstance(other, Reference):
15.69 + return cmp((self.kind, self.get_origin()), (other.kind, other.get_origin()))
15.70 + else:
15.71 + return cmp(str(self), other)
15.72 +
15.73 + def get_name(self):
15.74 +
15.75 + "Return the name used for this reference."
15.76 +
15.77 + return self.name
15.78 +
15.79 + def get_origin(self):
15.80 +
15.81 + "Return the origin of the reference."
15.82 +
15.83 + return self.kind != "<var>" and self.origin or None
15.84 +
15.85 + def get_kind(self):
15.86 +
15.87 + "Return the kind of object referenced."
15.88 +
15.89 + return self.kind
15.90 +
15.91 + def has_kind(self, kinds):
15.92 +
15.93 + """
15.94 + Return whether the reference describes an object from the given 'kinds',
15.95 + where such kinds may be "<class>", "<function>", "<instance>",
15.96 + "<module>" or "<var>".
15.97 + """
15.98 +
15.99 + if not isinstance(kinds, (list, tuple)):
15.100 + kinds = [kinds]
15.101 + return self.get_kind() in kinds
15.102 +
15.103 + def get_path(self):
15.104 +
15.105 + "Return the attribute names comprising the path to the origin."
15.106 +
15.107 + return self.get_origin().split(".")
15.108 +
15.109 + def static(self):
15.110 +
15.111 + "Return this reference if it refers to a static object, None otherwise."
15.112 +
15.113 + return not self.has_kind(["<var>", "<instance>"]) and self or None
15.114 +
15.115 + def final(self):
15.116 +
15.117 + "Return a reference to either a static object or None."
15.118 +
15.119 + static = self.static()
15.120 + return static and static.origin or None
15.121 +
15.122 + def instance_of(self):
15.123 +
15.124 + "Return a reference to an instance of the referenced class."
15.125 +
15.126 + return self.has_kind("<class>") and Reference("<instance>", self.origin) or None
15.127 +
15.128 + def as_var(self):
15.129 +
15.130 + """
15.131 + Return a variable version of this reference. Any origin information is
15.132 + discarded since variable references are deliberately ambiguous.
15.133 + """
15.134 +
15.135 + return Reference("<var>", None, self.name)
15.136 +
15.137 + def provided_by_module(self, module_name):
15.138 +
15.139 + "Return whether the reference is provided by 'module_name'."
15.140 +
15.141 + path = self.origin
15.142 + return not path or path.rsplit(".", 1)[0] == module_name
15.143 +
15.144 + def alias(self, name):
15.145 +
15.146 + "Alias this reference employing 'name'."
15.147 +
15.148 + return Reference(self.get_kind(), self.get_origin(), name)
15.149 +
15.150 +def decode_reference(s, name=None):
15.151 +
15.152 + "Decode 's', making a reference."
15.153 +
15.154 + if isinstance(s, Reference):
15.155 + return s.alias(name)
15.156 +
15.157 + # Null value.
15.158 +
15.159 + elif not s:
15.160 + return Reference("<var>", None, name)
15.161 +
15.162 + # Kind and origin.
15.163 +
15.164 + elif ":" in s:
15.165 + kind, origin = s.split(":")
15.166 + return Reference(kind, origin, name)
15.167 +
15.168 + # Kind-only, origin is indicated name.
15.169 +
15.170 + elif s[0] == "<":
15.171 + return Reference(s, name, name)
15.172 +
15.173 + # Module-only.
15.174 +
15.175 + else:
15.176 + return Reference("<module>", s, name)
15.177 +
15.178 +# vim: tabstop=4 expandtab shiftwidth=4