1.1 --- a/annotate.py Sun May 27 18:19:01 2007 +0200
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,1789 +0,0 @@
1.4 -#!/usr/bin/env python
1.5 -
1.6 -"""
1.7 -Annotate program node structures. The code in this module operates upon nodes
1.8 -which are produced when simplifying AST node trees originating from the compiler
1.9 -module.
1.10 -
1.11 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
1.12 -
1.13 -This software is free software; you can redistribute it and/or
1.14 -modify it under the terms of the GNU General Public License as
1.15 -published by the Free Software Foundation; either version 2 of
1.16 -the License, or (at your option) any later version.
1.17 -
1.18 -This software is distributed in the hope that it will be useful,
1.19 -but WITHOUT ANY WARRANTY; without even the implied warranty of
1.20 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.21 -GNU General Public License for more details.
1.22 -
1.23 -You should have received a copy of the GNU General Public
1.24 -License along with this library; see the file LICENCE.txt
1.25 -If not, write to the Free Software Foundation, Inc.,
1.26 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
1.27 -
1.28 ---------
1.29 -
1.30 -To use this module, the easiest approach is to use the load function:
1.31 -
1.32 -load(filename, builtins)
1.33 -
1.34 -To control module importing, an importer should be constructed and employed.
1.35 -Here, the standard path for module searching is used:
1.36 -
1.37 -importer = Importer(sys.path)
1.38 -load(filename, builtins, importer)
1.39 -
1.40 -Underneath the load function, the annotate function provides support for
1.41 -annotating modules already processed by simplify and fixnames:
1.42 -
1.43 -annotate(module, builtins)
1.44 -
1.45 -And at the most basic level, the most intricate approach involves obtaining an
1.46 -Annotator object:
1.47 -
1.48 -annotator = Annotator()
1.49 -
1.50 -Then, processing an existing module with it:
1.51 -
1.52 -annotator.process(module)
1.53 -
1.54 -If a module containing built-in classes and functions has already been
1.55 -annotated, such a module should be passed in as an additional argument:
1.56 -
1.57 -annotator.process(module, builtins)
1.58 -"""
1.59 -
1.60 -from simplified import *
1.61 -import simplify, fixnames # for the load function
1.62 -import compiler
1.63 -import os
1.64 -
1.65 -class System:
1.66 -
1.67 - """
1.68 - A class maintaining the state of the annotation system. When the system
1.69 - counter can no longer be incremented by any annotation operation, the
1.70 - system may be considered stable and fully annotated.
1.71 - """
1.72 -
1.73 - def __init__(self):
1.74 - self.count = 0
1.75 -
1.76 - def init(self, node, attr="types"):
1.77 -
1.78 - "Initialise a 'node' for annotation."
1.79 -
1.80 - if not hasattr(node, attr):
1.81 - setattr(node, attr, set())
1.82 -
1.83 - def annotate(self, node, types, attr="types"):
1.84 -
1.85 - "Annotate the given 'node' with the given 'types'."
1.86 -
1.87 - self.init(node, attr)
1.88 - self.combine(getattr(node, attr), types)
1.89 -
1.90 - def combine(self, target, types):
1.91 -
1.92 - """
1.93 - Combine the 'target' list with the given 'types', counting new members.
1.94 - """
1.95 -
1.96 - for type in types:
1.97 - if type not in target:
1.98 - target.add(type)
1.99 - self.count += 1
1.100 -
1.101 -system = System()
1.102 -
1.103 -# Exceptions.
1.104 -
1.105 -class AnnotationError(SimplifiedError):
1.106 -
1.107 - "An error in the annotation process."
1.108 -
1.109 - pass
1.110 -
1.111 -class AnnotationMessage(Exception):
1.112 -
1.113 - "A lesser annotation error."
1.114 -
1.115 - pass
1.116 -
1.117 -# Annotation.
1.118 -
1.119 -class Annotator(Visitor):
1.120 -
1.121 - """
1.122 - The type annotator which traverses the program nodes, typically depth-first,
1.123 - and maintains a record of the current set of types applying to the currently
1.124 - considered operation. Such types are also recorded on the nodes, and a
1.125 - special "system" record is maintained to monitor the level of annotation
1.126 - activity with a view to recognising when no more annotations are possible.
1.127 -
1.128 - Throughout the annotation activity, type information consists of lists of
1.129 - Attribute objects where such objects retain information about the context of
1.130 - the type (since a value in the program may be associated with an object or
1.131 - class) and the actual type of the value being manipulated. Upon accessing
1.132 - attribute information on namespaces, additional accessor information is also
1.133 - exchanged - this provides a means of distinguishing between the different
1.134 - types possible when the means of constructing the namespace may depend on
1.135 - run-time behaviour.
1.136 -
1.137 - Covered: Assign, CheckType, Conditional, Global, Import, InvokeRef,
1.138 - InvokeFunction, LoadAttr, LoadExc, LoadName, LoadRef, LoadTemp,
1.139 - Module, Not, Pass, Raise, ReleaseTemp, ReturnFromBlock,
1.140 - ReturnFromFunction, StoreAttr, StoreName, StoreTemp, Subprogram,
1.141 - Try.
1.142 - """
1.143 -
1.144 - def __init__(self, importer=None):
1.145 -
1.146 - "Initialise the visitor with an optional 'importer'."
1.147 -
1.148 - Visitor.__init__(self)
1.149 - self.system = system
1.150 - self.importer = importer or Importer()
1.151 -
1.152 - # Satisfy visitor issues.
1.153 -
1.154 - self.visitor = self
1.155 -
1.156 - def process(self, module, builtins=None):
1.157 -
1.158 - """
1.159 - Process the given 'module', using the optional 'builtins' to access
1.160 - built-in classes and functions.
1.161 - """
1.162 -
1.163 - self.subprograms = []
1.164 - self.current_subprograms = []
1.165 - self.current_namespaces = []
1.166 - self.rerun_subprograms = {}
1.167 - self.namespace = None
1.168 - self.module = module
1.169 -
1.170 - # Process the module, supplying builtins if possible.
1.171 -
1.172 - self.builtins = builtins
1.173 - self.global_namespace = Namespace()
1.174 -
1.175 - if builtins is not None:
1.176 - self.builtins_namespace = builtins.namespace
1.177 - else:
1.178 - self.builtins_namespace = self.global_namespace
1.179 -
1.180 - # NOTE: Not declaring module namespace usage, even though it is used.
1.181 -
1.182 - self.process_node(module, self.global_namespace, 0)
1.183 -
1.184 - def process_node(self, node, locals, using_module_namespace):
1.185 -
1.186 - """
1.187 - Process a subprogram or module 'node', indicating the initial 'locals'.
1.188 - Note that this method may mutate nodes in the original program.
1.189 - """
1.190 -
1.191 - # Recursion test.
1.192 -
1.193 - if node in self.current_subprograms:
1.194 - if not self.rerun_subprograms.has_key(node):
1.195 - self.rerun_subprograms[node] = []
1.196 - self.rerun_subprograms[node].append(locals)
1.197 - return
1.198 -
1.199 - # Record the current subprogram and namespace.
1.200 -
1.201 - self.current_subprograms.append(node)
1.202 -
1.203 - # Determine the namespace.
1.204 -
1.205 - self.current_namespaces.append(self.namespace)
1.206 - self.namespace = locals
1.207 -
1.208 - # Add namespace details to any structure involved.
1.209 -
1.210 - if getattr(node, "structure", None) is not None:
1.211 - node.structure.namespace = Namespace()
1.212 -
1.213 - # Initialise bases where appropriate.
1.214 -
1.215 - if hasattr(node.structure, "bases"):
1.216 - base_refs = []
1.217 - for base in node.structure.bases:
1.218 - self.dispatch(base)
1.219 - base_refs.append(self.namespace.types)
1.220 - node.structure.base_refs = base_refs
1.221 -
1.222 - # Dispatch to the code itself.
1.223 -
1.224 - node.namespace = self.namespace
1.225 - self.set_module_namespace(using_module_namespace)
1.226 -
1.227 - self.dispatch(node)
1.228 - self.extract_results(node)
1.229 -
1.230 - while self.rerun_subprograms.has_key(node):
1.231 - all_rerun_locals = self.rerun_subprograms[node]
1.232 - del self.rerun_subprograms[node]
1.233 - for rerun_locals in all_rerun_locals:
1.234 - #print "Re-running", node, "with", rerun_locals
1.235 -
1.236 - self.namespace = rerun_locals
1.237 - node.namespace = rerun_locals
1.238 - self.set_module_namespace(using_module_namespace)
1.239 -
1.240 - self.dispatch(node)
1.241 - self.extract_results(node)
1.242 -
1.243 - # Restore the previous subprogram and namespace.
1.244 -
1.245 - self.namespace = self.current_namespaces.pop()
1.246 - self.current_subprograms.pop()
1.247 - self.reset_module_namespace(using_module_namespace)
1.248 -
1.249 - def set_module_namespace(self, using_module_namespace):
1.250 -
1.251 - """
1.252 - In order to keep global accesses working, the module namespace must be
1.253 - adjusted.
1.254 - """
1.255 -
1.256 - if using_module_namespace:
1.257 - self.module.namespace = self.namespace
1.258 -
1.259 - def reset_module_namespace(self, using_module_namespace):
1.260 -
1.261 - """
1.262 - In order to keep global accesses working, the module namespace must be
1.263 - reset.
1.264 - """
1.265 -
1.266 - if using_module_namespace:
1.267 - self.module.namespace = self.namespace
1.268 -
1.269 - def extract_results(self, node):
1.270 -
1.271 - "Extract results from the namespace."
1.272 -
1.273 - node.namespace = self.namespace
1.274 - self.system.annotate(node, self.namespace.raises, "raises")
1.275 - self.system.annotate(node, self.namespace.returns, "returns")
1.276 - if hasattr(node, "return_locals"):
1.277 - node.return_locals.update(self.namespace.return_locals)
1.278 -
1.279 - def annotate(self, node, types=None):
1.280 -
1.281 - """
1.282 - Annotate the given 'node' in the system, using either the optional
1.283 - 'types' or the namespace's current type information.
1.284 - """
1.285 -
1.286 - if types is None:
1.287 - self.system.annotate(node, self.namespace.types)
1.288 - else:
1.289 - self.system.annotate(node, types)
1.290 -
1.291 - def annotate_parameters(self, node, items):
1.292 -
1.293 - """
1.294 - Annotate the given 'node' using the given 'items' and updating the
1.295 - system's annotation counter.
1.296 - """
1.297 -
1.298 - if not hasattr(node, "paramtypes"):
1.299 - node.paramtypes = {}
1.300 -
1.301 - for param, types in items:
1.302 - if not node.paramtypes.has_key(param):
1.303 - node.paramtypes[param] = set()
1.304 - self.system.combine(node.paramtypes[param], types)
1.305 -
1.306 - # Visitor methods.
1.307 -
1.308 - def default(self, node):
1.309 -
1.310 - """
1.311 - Process the given 'node', given that it does not have a specific
1.312 - handler.
1.313 - """
1.314 -
1.315 - raise AnnotationMessage, "Node '%s' not supported." % node
1.316 -
1.317 - def dispatch(self, node, *args):
1.318 - try:
1.319 - Visitor.dispatch(self, node, *args)
1.320 - except AnnotationError, exc:
1.321 - exc.add(node)
1.322 - raise
1.323 - except AnnotationMessage, exc:
1.324 - raise AnnotationError(exc, node)
1.325 -
1.326 - # Specific node methods.
1.327 -
1.328 - def visitAssign(self, assign):
1.329 -
1.330 - """
1.331 - Process the 'assign' node and its contents.
1.332 - """
1.333 -
1.334 - self.dispatches(assign.code)
1.335 -
1.336 - def visitCheckType(self, checktype):
1.337 -
1.338 - """
1.339 - Process the 'checktype' node, finding the possible types of the
1.340 - exception, and processing each choice to build a list of checked types
1.341 - for the exception.
1.342 - """
1.343 -
1.344 - inverted = getattr(checktype, "inverted", 0)
1.345 - self.dispatch(checktype.expr)
1.346 -
1.347 - expr_types = self.namespace.types
1.348 - choice_types = set()
1.349 - choices = []
1.350 -
1.351 - for choice in checktype.choices:
1.352 - choices.append(self.dispatch(choice))
1.353 - choice_types.update(self.namespace.types)
1.354 -
1.355 - for expr_type in expr_types:
1.356 - in_choices = expr_type.type.get_class() in choice_types
1.357 -
1.358 - # Filter out types not in the choices list unless the operation is
1.359 - # inverted; in which case, filter out types in the choices list.
1.360 -
1.361 - if not inverted and not in_choices or inverted and in_choices:
1.362 - self._prune_non_accesses(checktype.expr, expr_type)
1.363 -
1.364 - def visitConditional(self, conditional):
1.365 -
1.366 - """
1.367 - Process the 'conditional' node, processing the test, body and else
1.368 - clauses and recording their processed forms. The body and else clauses
1.369 - are processed within their own namespaces, and the test is also
1.370 - processed in its own namespace if 'isolate_test' is set on the
1.371 - 'conditional' node.
1.372 - """
1.373 -
1.374 - # Conditionals keep local namespace changes isolated.
1.375 - # With Return nodes inside the body/else sections, the changes are
1.376 - # communicated to the caller.
1.377 -
1.378 - is_module = self.namespace is self.module.namespace
1.379 -
1.380 - # Where the test is closely associated with the body, save the namespace
1.381 - # before entering the test.
1.382 -
1.383 - if conditional.isolate_test:
1.384 - saved_namespace = self.namespace
1.385 - self.namespace = Namespace()
1.386 - if is_module:
1.387 - self.module.namespace = self.namespace
1.388 - self.namespace.merge_namespace(saved_namespace)
1.389 -
1.390 - self.dispatch(conditional.test)
1.391 -
1.392 - # Where the test may affect the body and the else clause, save the
1.393 - # namespace after processing the test.
1.394 -
1.395 - if not conditional.isolate_test:
1.396 - saved_namespace = self.namespace
1.397 - self.namespace = Namespace()
1.398 - if is_module:
1.399 - self.module.namespace = self.namespace
1.400 - self.namespace.merge_namespace(saved_namespace)
1.401 -
1.402 - # NOTE: Exception recording.
1.403 -
1.404 - else:
1.405 - test_raises = set()
1.406 - test_raises.update(self.namespace.raises)
1.407 -
1.408 - # Process the body clause.
1.409 -
1.410 - self.dispatches(conditional.body)
1.411 - body_namespace = self.namespace
1.412 -
1.413 - # Use the saved namespace as a template for the else clause.
1.414 -
1.415 - self.namespace = Namespace()
1.416 - if is_module:
1.417 - self.module.namespace = self.namespace
1.418 - self.namespace.merge_namespace(saved_namespace)
1.419 -
1.420 - # Process the else clause.
1.421 -
1.422 - self.dispatches(conditional.else_)
1.423 - else_namespace = self.namespace
1.424 -
1.425 - # Merge the body and else namespaces.
1.426 -
1.427 - self.namespace = Namespace()
1.428 - if is_module:
1.429 - self.module.namespace = self.namespace
1.430 - self.namespace.merge_namespace(body_namespace)
1.431 - self.namespace.merge_namespace(else_namespace)
1.432 -
1.433 - # NOTE: Test of exception type pruning based on the test/body.
1.434 - # Note that the checked exceptions are tested for re-raising.
1.435 -
1.436 - if conditional.isolate_test:
1.437 - for exc_type in test_raises:
1.438 - if exc_type not in body_namespace.raises:
1.439 - self.namespace.revoke_exception_type(exc_type)
1.440 -
1.441 - def visitGlobal(self, global_):
1.442 -
1.443 - """
1.444 - Leave the 'global_' node unprocessed since namespaces should have
1.445 - already been altered to take global names into consideration.
1.446 - """
1.447 -
1.448 - pass
1.449 -
1.450 - def visitImport(self, import_):
1.451 -
1.452 - """
1.453 - Process the 'import_' node, importing the module with the stated name
1.454 - and storing details on the node.
1.455 - """
1.456 -
1.457 - module = self.importer.load(import_.name, self.builtins, getattr(import_, "alias", None))
1.458 - if module is not None:
1.459 - self.namespace.set_types(set([module]))
1.460 - else:
1.461 - self.namespace.set_types(set())
1.462 - self.annotate(import_) # mainly for viewing purposes
1.463 -
1.464 - def _visitInvoke(self, invoke, invocation_types, have_args):
1.465 -
1.466 - """
1.467 - Process the 'invoke' node, using the given 'invocation_types' as the
1.468 - list of callables to be investigated for instantiation or for the
1.469 - invocation of functions or blocks. If 'have_args' is a true value, any
1.470 - invocation or instantiation will involve arguments.
1.471 - """
1.472 -
1.473 - # Now locate and invoke the subprogram. This can be complicated because
1.474 - # the target may be a class or object, and there may be many different
1.475 - # related subprograms.
1.476 -
1.477 - invocations = []
1.478 -
1.479 - # Visit each callable in turn, finding subprograms.
1.480 -
1.481 - for attr in invocation_types:
1.482 -
1.483 - # Deal with class invocations by providing instance objects.
1.484 - # Here, each class is queried for the __init__ method, which may
1.485 - # exist for some combinations of classes in a hierarchy but not for
1.486 - # others.
1.487 -
1.488 - if isinstance(attr.type, Class):
1.489 - attributes = get_attributes(attr.type, "__init__")
1.490 -
1.491 - # Deal with object invocations by using __call__ methods.
1.492 -
1.493 - elif isinstance(attr.type, Instance):
1.494 - attributes = get_attributes(attr.type, "__call__")
1.495 -
1.496 - # Normal functions or methods are more straightforward.
1.497 - # Here, we model them using an attribute with no context and with
1.498 - # no associated accessor.
1.499 -
1.500 - else:
1.501 - attributes = [(attr, None)]
1.502 -
1.503 - # Inspect each attribute and extract the subprogram.
1.504 -
1.505 - for attribute, accessor in attributes:
1.506 -
1.507 - # If a class is involved, presume that it must create a new
1.508 - # object.
1.509 -
1.510 - if isinstance(attr.type, Class):
1.511 -
1.512 - # Instantiate the class.
1.513 -
1.514 - instance = self.new_instance(invoke, attr.type)
1.515 -
1.516 - # For instantiations, switch the context.
1.517 -
1.518 - if attribute is not None:
1.519 - attribute = Attribute(instance, attribute.type)
1.520 -
1.521 - # Request an instance-specific initialiser.
1.522 -
1.523 - attribute = attr.type.get_attribute_for_instance(attribute, instance)
1.524 -
1.525 - # Skip cases where no callable is found.
1.526 -
1.527 - if attribute is not None:
1.528 -
1.529 - # If a subprogram is defined, invoke it.
1.530 -
1.531 - self.invoke_subprogram(invoke, attribute)
1.532 - if attribute.type not in invocations:
1.533 - invocations.append(attribute.type)
1.534 -
1.535 - elif not isinstance(attr.type, Class):
1.536 - print "Invocation type is None for", accessor
1.537 -
1.538 - else:
1.539 -
1.540 - # Test to see if no arguments were supplied in cases where no
1.541 - # initialiser was found.
1.542 -
1.543 - if have_args:
1.544 - raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type
1.545 -
1.546 - # Special case: initialisation.
1.547 -
1.548 - if isinstance(attr.type, Class):
1.549 -
1.550 - # Associate the instance with the result of this invocation.
1.551 -
1.552 - self.namespace.set_types(set([Attribute(None, instance)]))
1.553 - self.annotate(invoke)
1.554 -
1.555 - # Remember the invocations that were found, along with the return type
1.556 - # information.
1.557 -
1.558 - invoke.invocations = invocations
1.559 - self.namespace.set_types(getattr(invoke, "types", set()))
1.560 -
1.561 - def visitInvokeRef(self, invoke):
1.562 -
1.563 - """
1.564 - Process the 'invoke' node, first finding the callables indicated by the
1.565 - reference.
1.566 - """
1.567 -
1.568 - # Where the invocation belongs to an instance but the invoked subprogram
1.569 - # does not, request a special copy.
1.570 -
1.571 - instance = getattr(invoke, "instance", None)
1.572 - if instance is not None and getattr(invoke.ref, "instance", None) is None:
1.573 - if invoke.ref.copies.has_key(instance):
1.574 - invoke.ref = invoke.ref.copies[instance]
1.575 - else:
1.576 - invoke.ref = invoke.ref.copy(instance)
1.577 - #print "Created", invoke.ref, "for", getattr(invoke.ref, "instance", None)
1.578 - invoke.ref.module.simplifier.subnames[invoke.ref.full_name()] = invoke.ref
1.579 - invocation_types = [Attribute(None, invoke.ref)]
1.580 - self._visitInvoke(invoke, invocation_types, have_args=0)
1.581 -
1.582 - def visitInvokeFunction(self, invoke):
1.583 -
1.584 - """
1.585 - Process the 'invoke' node, first finding the callables indicated by the
1.586 - expression.
1.587 - """
1.588 -
1.589 - self.dispatch(invoke.expr)
1.590 - invocation_types = self.namespace.types
1.591 -
1.592 - # Invocation processing starts with making sure that the arguments have
1.593 - # been processed.
1.594 -
1.595 - self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke))
1.596 -
1.597 - def visitLoadAttr(self, loadattr):
1.598 -
1.599 - """
1.600 - Process the 'loadattr' node, processing and storing the expression, and
1.601 - using the expression's types to construct records of accesses and
1.602 - non-accesses using the stated attribute name.
1.603 - """
1.604 -
1.605 - self.dispatch(loadattr.expr)
1.606 - types = set()
1.607 - raises = set()
1.608 - non_accesses = []
1.609 - accesses = {}
1.610 -
1.611 - # For each expression type...
1.612 -
1.613 - for attr in self.namespace.types:
1.614 -
1.615 - # Find types for the named attribute.
1.616 -
1.617 - attributes = get_attributes(attr.type, loadattr.name)
1.618 -
1.619 - # Where no attributes exist...
1.620 -
1.621 - if not attributes:
1.622 -
1.623 - # Register new invalid accesses and mark a possible exception.
1.624 -
1.625 - if not attr in non_accesses:
1.626 - non_accesses.append(attr)
1.627 - exc = self.get_builtin_instances(loadattr, "AttributeError")
1.628 - raises.update(exc)
1.629 - self.namespace.raises.update(exc)
1.630 -
1.631 - # Revoke this type from any name involved.
1.632 -
1.633 - self._prune_non_accesses(loadattr.expr, attr)
1.634 -
1.635 - # For each type found...
1.636 -
1.637 - for attribute, accessor in attributes:
1.638 -
1.639 - # For actual attributes, register the type and remember the
1.640 - # access.
1.641 -
1.642 - if attribute is not None:
1.643 - types.add(attribute)
1.644 - if not accesses.has_key(attr.type):
1.645 - accesses[attr.type] = []
1.646 - if not (attribute, accessor) in accesses[attr.type]:
1.647 - accesses[attr.type].append((attribute, accessor))
1.648 -
1.649 - # Otherwise, register new invalid accesses and note a possible
1.650 - # exception.
1.651 -
1.652 - else:
1.653 - if not attr in non_accesses:
1.654 - non_accesses.append(attr)
1.655 - exc = self.get_builtin_instances(loadattr, "AttributeError")
1.656 - raises.update(exc)
1.657 - self.namespace.raises.update(exc)
1.658 -
1.659 - # Revoke this type from any name involved.
1.660 -
1.661 - self._prune_non_accesses(loadattr.expr, attr)
1.662 -
1.663 - if not types:
1.664 - print "No attribute found for", loadattr.name, "given", self.namespace.types
1.665 -
1.666 - # Remember the result types.
1.667 -
1.668 - self.namespace.set_types(types)
1.669 - loadattr.non_accesses = non_accesses
1.670 - loadattr.accesses = accesses
1.671 - loadattr.raises = raises
1.672 - self.annotate(loadattr)
1.673 -
1.674 - def _prune_non_accesses(self, expr, attr):
1.675 -
1.676 - """
1.677 - Prune type information from 'expr' where the given 'attr' has been
1.678 - shown to be a non-access.
1.679 - """
1.680 -
1.681 - if isinstance(expr, LoadName):
1.682 - self.namespace.revoke(expr.name, attr)
1.683 - elif isinstance(expr, LoadExc):
1.684 - self.namespace.revoke_exception_type(attr)
1.685 - elif isinstance(expr, LoadTemp):
1.686 - self.namespace.revoke_temp_type(getattr(expr, "index", None), attr)
1.687 -
1.688 - # LoadAttr cannot be pruned since this might unintentionally prune
1.689 - # legitimate types from other applications of the referenced type, it
1.690 - # almost certainly doesn't take "concurrent" mutation into
1.691 - # consideration (where in a running program, the pruned type is actually
1.692 - # reintroduced, making the pruning invalid), and there is no easy way of
1.693 - # preserving the meaning of a namespace without either creating lots of
1.694 - # specialised instances, and even then...
1.695 -
1.696 - #elif isinstance(expr, LoadAttr):
1.697 - # for expr_attr in expr.expr.types:
1.698 - # if hasattr(expr_attr.type, "namespace"):
1.699 - # expr_attr.type.namespace.revoke(expr.name, attr)
1.700 -
1.701 - def visitLoadExc(self, loadexc):
1.702 -
1.703 - """
1.704 - Process the 'loadexc' node, discovering the possible exception types
1.705 - raised.
1.706 - """
1.707 -
1.708 - self.namespace.set_types(self.namespace.raises)
1.709 - self.annotate(loadexc)
1.710 -
1.711 - def visitLoadName(self, loadname):
1.712 -
1.713 - """
1.714 - Process the 'loadname' node, processing the name information on the node
1.715 - to determine which types are involved with the name.
1.716 - """
1.717 -
1.718 - self.namespace.set_types(self.namespace.load(loadname.name))
1.719 - self.annotate(loadname)
1.720 -
1.721 - def visitLoadRef(self, loadref):
1.722 -
1.723 - """
1.724 - Process the 'loadref' node, obtaining type information about the
1.725 - reference stated on the node.
1.726 - """
1.727 -
1.728 - self.namespace.set_types(set([Attribute(None, loadref.ref)]))
1.729 - self.annotate(loadref)
1.730 -
1.731 - def visitLoadTemp(self, loadtemp):
1.732 -
1.733 - """
1.734 - Process the 'loadtemp' node, obtaining type information about the
1.735 - temporary variable accessed, and removing variable information where the
1.736 - 'release' attribute has been set on the node.
1.737 - """
1.738 -
1.739 - index = getattr(loadtemp, "index", None)
1.740 - try:
1.741 - if getattr(loadtemp, "release", 0):
1.742 - self.namespace.set_types(self.namespace.temp[index].pop())
1.743 - else:
1.744 - self.namespace.set_types(self.namespace.temp[index][-1])
1.745 - except KeyError:
1.746 - raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.747 - self.annotate(loadtemp)
1.748 -
1.749 - def visitMakeTuple(self, maketuple):
1.750 -
1.751 - """
1.752 - Process the 'maketuple' node and its contents.
1.753 - """
1.754 -
1.755 - # Get a tuple and populate it with type information for the contents.
1.756 -
1.757 - tuples = self.get_builtin_instances(maketuple, "tuple")
1.758 -
1.759 - # NOTE: This is dependent on the tuple definition in the builtins.
1.760 -
1.761 - for node in maketuple.nodes:
1.762 - self.dispatch(node)
1.763 - for t in tuples:
1.764 - t.type.namespace.add("value", self.namespace.types)
1.765 -
1.766 - self.namespace.set_types(tuples)
1.767 - self.annotate(maketuple)
1.768 -
1.769 - def visitModule(self, module):
1.770 -
1.771 - """
1.772 - Process the 'module' and its contents.
1.773 - """
1.774 -
1.775 - self.dispatches(module.code)
1.776 -
1.777 - def visitNot(self, not_):
1.778 -
1.779 - "Process the 'not_' node and its expression."
1.780 -
1.781 - self.dispatch(not_.expr)
1.782 -
1.783 - def visitPass(self, pass_):
1.784 -
1.785 - "Leave the 'pass_' node unprocessed."
1.786 -
1.787 - pass
1.788 -
1.789 - def visitRaise(self, raise_):
1.790 -
1.791 - """
1.792 - Process the 'raise_' node, processing any traceback information along
1.793 - with the raised exception expression, converting the node into a kind of
1.794 - invocation where the expression is found not to be an invocation itself.
1.795 - This node affects the namespace, adding exception types to the list of
1.796 - those raised in the namespace.
1.797 - """
1.798 -
1.799 - if getattr(raise_, "traceback", None) is not None:
1.800 - self.dispatch(raise_.traceback)
1.801 - self.dispatch(raise_.expr)
1.802 -
1.803 - # Handle bare name exceptions by converting any classes to instances.
1.804 -
1.805 - if not isinstance(raise_.expr, InvokeFunction):
1.806 - raise_.pos_args = []
1.807 - raise_.kw_args = {}
1.808 - raise_.star = None
1.809 - raise_.dstar = None
1.810 - types = set()
1.811 - for attr in self.namespace.types:
1.812 - if isinstance(attr.type, Class):
1.813 - self._visitInvoke(raise_, [attr], have_args=0)
1.814 - types.update(self.namespace.types)
1.815 - else:
1.816 - types = self.namespace.types
1.817 -
1.818 - self.namespace.raises.update(types)
1.819 -
1.820 - def visitReleaseTemp(self, releasetemp):
1.821 -
1.822 - """
1.823 - Process the 'releasetemp' node, removing temporary variable information
1.824 - from the current namespace.
1.825 - """
1.826 -
1.827 - index = getattr(releasetemp, "index", None)
1.828 - try:
1.829 - self.namespace.temp[index].pop()
1.830 - except KeyError:
1.831 - raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.832 - except IndexError:
1.833 - pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index
1.834 -
1.835 - def visitResetExc(self, resetexc):
1.836 - self.namespace.raises = set()
1.837 -
1.838 - def visitReturn(self, return_):
1.839 -
1.840 - """
1.841 - Process the 'return_' node, processing any expression and obtaining type
1.842 - information to be accumulated in the current namespace's list of return
1.843 - types. A snapshot of the namespace is taken for the purposes of
1.844 - reconciling or merging namespaces where subprograms actually share
1.845 - locals with their callers.
1.846 - """
1.847 -
1.848 - if hasattr(return_, "expr"):
1.849 - self.dispatch(return_.expr)
1.850 - self.namespace.returns.update(self.namespace.types)
1.851 - self.annotate(return_)
1.852 - self.namespace.snapshot()
1.853 -
1.854 - visitReturnFromBlock = visitReturn
1.855 - visitReturnFromFunction = visitReturn
1.856 -
1.857 - def visitStoreAttr(self, storeattr):
1.858 -
1.859 - """
1.860 - Process the 'storeattr' node, processing the expression and target, and
1.861 - using the type information obtained to build records of legitimate
1.862 - writes to the stated attribute, along with "impossible" non-writes to
1.863 - the attribute.
1.864 - """
1.865 -
1.866 - self.dispatch(storeattr.expr)
1.867 - expr = self.namespace.types
1.868 - self.dispatch(storeattr.lvalue)
1.869 - writes = {}
1.870 - non_writes = []
1.871 - for attr in self.namespace.types:
1.872 - # NOTE: Impose "atomic" constraints on certain types.
1.873 - if attr is None:
1.874 - if not attr in non_writes:
1.875 - non_writes.append(attr)
1.876 - continue
1.877 - attr.type.namespace.add(storeattr.name, expr)
1.878 - writes[attr.type] = attr.type.namespace.load(storeattr.name)
1.879 - if not writes:
1.880 - print "Unable to store attribute", storeattr.name, "given", self.namespace.types
1.881 - storeattr.writes = writes
1.882 - storeattr.non_writes = non_writes
1.883 -
1.884 - def visitStoreName(self, storename):
1.885 -
1.886 - """
1.887 - Process the 'storename' node, processing the expression on the node and
1.888 - associating the type information obtained with the stated name in the
1.889 - current namespace.
1.890 - """
1.891 -
1.892 - self.dispatch(storename.expr)
1.893 - self.namespace.store(storename.name, self.namespace.types)
1.894 - self.annotate(storename)
1.895 -
1.896 - def visitStoreTemp(self, storetemp):
1.897 -
1.898 - """
1.899 - Process the 'storetemp' node, processing the expression on the node and
1.900 - associating the type information obtained with a temporary variable in
1.901 - the current namespace.
1.902 - """
1.903 -
1.904 - self.dispatch(storetemp.expr)
1.905 - index = getattr(storetemp, "index", None)
1.906 - if not self.namespace.temp.has_key(index):
1.907 - self.namespace.temp[index] = []
1.908 - self.namespace.temp[index].append(self.namespace.types)
1.909 -
1.910 - def visitSubprogram(self, subprogram):
1.911 -
1.912 - """
1.913 - Process the 'subprogram' node, processing its contents (a group of nodes
1.914 - comprising the subprogram).
1.915 - """
1.916 -
1.917 - self.dispatches(subprogram.code)
1.918 -
1.919 - def visitTry(self, try_):
1.920 -
1.921 - """
1.922 - Process the 'try_' node, processing the body clause in its own namespace
1.923 - derived from the current namespace, processing any handler clause using
1.924 - the namespace information accumulated in the body, and processing any
1.925 - else and finally clauses, attempting to supply each with appropriate
1.926 - namespace information.
1.927 - """
1.928 -
1.929 - is_module = self.namespace is self.module.namespace
1.930 -
1.931 - self.dispatches(try_.body)
1.932 -
1.933 - # Save the namespace from the body.
1.934 -
1.935 - body_namespace = Namespace()
1.936 - body_namespace.merge_namespace(self.namespace)
1.937 -
1.938 - # Process the handler.
1.939 -
1.940 - if hasattr(try_, "handler"):
1.941 - self.dispatches(try_.handler)
1.942 -
1.943 - # Save the namespace from the handler.
1.944 -
1.945 - handler_namespace = Namespace()
1.946 - handler_namespace.merge_namespace(self.namespace)
1.947 -
1.948 - # Remember the raised exceptions encountered so far.
1.949 -
1.950 - raises = self.namespace.raises
1.951 -
1.952 - # Process the else clause.
1.953 -
1.954 - if hasattr(try_, "else_"):
1.955 -
1.956 - # Restore the body namespace for the else clause.
1.957 -
1.958 - self.namespace = body_namespace
1.959 - if is_module:
1.960 - self.module.namespace = self.namespace
1.961 -
1.962 - # Empty the raised exceptions for the else clause.
1.963 -
1.964 - self.namespace.raises = set()
1.965 - self.dispatches(try_.else_)
1.966 - self.namespace.raises = raises
1.967 -
1.968 - # Merge the namespaces.
1.969 -
1.970 - self.namespace = Namespace()
1.971 - if is_module:
1.972 - self.module.namespace = self.namespace
1.973 - self.namespace.merge_namespace(body_namespace)
1.974 - self.namespace.merge_namespace(handler_namespace)
1.975 -
1.976 - # Process the finally clause, if any.
1.977 -
1.978 - self.dispatches(try_.finally_)
1.979 -
1.980 - def visitYield(self, yield_):
1.981 - raise NotImplementedError, "The yield statement is not currently supported."
1.982 -
1.983 - # Utility methods.
1.984 -
1.985 - def get_builtin_instances(self, node, name):
1.986 - return set([Attribute(None, self.new_instance(node, attr.type)) for attr in self.builtins.namespace[name]])
1.987 -
1.988 - def new_instance(self, node, type):
1.989 -
1.990 - "For the given 'node', obtain an instance from the given 'type'."
1.991 -
1.992 - if not type.has_instance(node):
1.993 - instance = Instance()
1.994 - instance.namespace = Namespace()
1.995 - instance.namespace.store("__class__", set([Attribute(None, type)]))
1.996 - type.add_instance(node, instance)
1.997 - else:
1.998 - instance = type.get_instance(node)
1.999 -
1.1000 - return instance
1.1001 -
1.1002 - def invoke_subprogram(self, invoke, attribute):
1.1003 -
1.1004 - """
1.1005 - Invoke using the given 'invoke' node the subprogram represented by the
1.1006 - given 'attribute'.
1.1007 - """
1.1008 -
1.1009 - # Test for context information, making it into a real attribute.
1.1010 -
1.1011 - if attribute.context is not None:
1.1012 - context = Attribute(None, attribute.context)
1.1013 - target = attribute.type
1.1014 - else:
1.1015 - context = None
1.1016 - target = attribute.type
1.1017 -
1.1018 - # Test to see if anything has changed.
1.1019 -
1.1020 - if hasattr(invoke, "syscount") and invoke.syscount.has_key(target) and invoke.syscount[target] == self.system.count:
1.1021 - return
1.1022 -
1.1023 - # Remember the state of the system.
1.1024 -
1.1025 - else:
1.1026 - if not hasattr(invoke, "syscount"):
1.1027 - invoke.syscount = {}
1.1028 - invoke.syscount[target] = self.system.count
1.1029 -
1.1030 - # Provide the correct namespace for the invocation.
1.1031 - # This may be a "shared" namespace...
1.1032 -
1.1033 - if getattr(invoke, "share_locals", 0):
1.1034 - namespace = Namespace()
1.1035 - namespace.merge_namespace(self.namespace, everything=0)
1.1036 - using_module_namespace = self.namespace is self.module.namespace
1.1037 -
1.1038 - # Or it may be a structure...
1.1039 -
1.1040 - elif getattr(target, "structure", None):
1.1041 - namespace = Namespace()
1.1042 - using_module_namespace = 0
1.1043 -
1.1044 - # Or it may be a new namespace populated with the supplied parameters.
1.1045 -
1.1046 - else:
1.1047 - items = self.make_items(invoke, target, context)
1.1048 - namespace = Namespace()
1.1049 - namespace.merge_items(items)
1.1050 - using_module_namespace = 0
1.1051 -
1.1052 - # NOTE: Avoid PEP 227 (nested scopes) whilst permitting references to a
1.1053 - # NOTE: subprogram within itself. Do not define the name of the function
1.1054 - # NOTE: within a method definition.
1.1055 -
1.1056 - if getattr(target, "name", None) is not None and not getattr(target, "is_method", 0):
1.1057 - namespace.store(target.name, set([Attribute(None, target)]))
1.1058 -
1.1059 - # Process the subprogram.
1.1060 -
1.1061 - self.process_node(target, namespace, using_module_namespace)
1.1062 -
1.1063 - # NOTE: Improve and verify this.
1.1064 - # If the invocation returns a value, acquire the return types.
1.1065 -
1.1066 - if getattr(target, "returns_value", 0):
1.1067 - self.namespace.set_types(target.returns)
1.1068 - self.annotate(invoke)
1.1069 -
1.1070 - # If it is a normal block, merge the locals.
1.1071 - # This can happen in addition to the above because for things like
1.1072 - # logical expressions, the namespace can be modified whilst values are
1.1073 - # returned as results.
1.1074 -
1.1075 - if getattr(invoke, "share_locals", 0):
1.1076 - self.namespace.reset()
1.1077 -
1.1078 - # Merge the locals snapshots.
1.1079 -
1.1080 - for locals in target.return_locals:
1.1081 -
1.1082 - # For blocks returning values (such as operations), do not merge
1.1083 - # snapshots or results.
1.1084 -
1.1085 - if getattr(target, "returns_value", 0):
1.1086 - self.namespace.merge_namespace(locals, everything=0)
1.1087 -
1.1088 - # For blocks not returning values (such as loops), merge
1.1089 - # snapshots and results since they contain details of genuine
1.1090 - # returns.
1.1091 -
1.1092 - else:
1.1093 - self.namespace.merge_namespace(locals)
1.1094 -
1.1095 - # Incorporate any raised exceptions.
1.1096 -
1.1097 - if not hasattr(invoke, "raises"):
1.1098 - invoke.raises = set()
1.1099 - invoke.raises.update(target.raises)
1.1100 - self.namespace.raises.update(target.raises)
1.1101 -
1.1102 - def process_args(self, invocation):
1.1103 -
1.1104 - """
1.1105 - Process the arguments associated with an 'invocation'. Return whether
1.1106 - any arguments were processed.
1.1107 - """
1.1108 -
1.1109 - self.dispatches(invocation.pos_args)
1.1110 - self.dispatch_dict(invocation.kw_args)
1.1111 -
1.1112 - # Get type information for star and dstar arguments.
1.1113 -
1.1114 - if invocation.star is not None:
1.1115 - param, default = invocation.star
1.1116 - self.dispatch(default)
1.1117 - invocation.star = param, default
1.1118 -
1.1119 - if invocation.dstar is not None:
1.1120 - param, default = invocation.dstar
1.1121 - self.dispatch(default)
1.1122 - invocation.dstar = param, default
1.1123 -
1.1124 - if invocation.pos_args or invocation.kw_args or invocation.star or invocation.dstar:
1.1125 - return 1
1.1126 - else:
1.1127 - return 0
1.1128 -
1.1129 - def make_items(self, invocation, subprogram, context):
1.1130 -
1.1131 - """
1.1132 - Make an items mapping for the 'invocation' of the 'subprogram' using the
1.1133 - given 'context' (which may be None).
1.1134 - """
1.1135 -
1.1136 - # NOTE: Support class methods!
1.1137 -
1.1138 - if context is not None and isinstance(context.type, Instance):
1.1139 - pos_args = [Self(context)] + invocation.pos_args
1.1140 - else:
1.1141 - pos_args = invocation.pos_args
1.1142 -
1.1143 - # Duplicate the keyword arguments - we remove them in processing below.
1.1144 -
1.1145 - kw_args = {}
1.1146 - kw_args.update(invocation.kw_args)
1.1147 -
1.1148 - # Sort the arguments into positional and keyword arguments.
1.1149 -
1.1150 - params = subprogram.params
1.1151 - items = []
1.1152 - star_args = []
1.1153 -
1.1154 - # Match each positional argument, taking excess arguments as star args.
1.1155 -
1.1156 - for arg in pos_args:
1.1157 - if params:
1.1158 - param, default = params[0]
1.1159 - if arg is None:
1.1160 - arg = default
1.1161 - if hasattr(arg, "types"):
1.1162 - items.append((param, arg.types))
1.1163 - else:
1.1164 - items.append((param, set())) # Annotation has not succeeded.
1.1165 - params = params[1:]
1.1166 - else:
1.1167 - star_args.append(arg)
1.1168 -
1.1169 - # Collect the remaining defaults.
1.1170 -
1.1171 - while params:
1.1172 - param, default = params[0]
1.1173 - if kw_args.has_key(param):
1.1174 - arg = kw_args[param]
1.1175 - del kw_args[param]
1.1176 - elif default is not None:
1.1177 - self.dispatch(default)
1.1178 - arg = default
1.1179 - else:
1.1180 - raise AnnotationMessage, "No argument supplied in '%s' for parameter '%s'." % (subprogram, param)
1.1181 - if hasattr(arg, "types"):
1.1182 - items.append((param, arg.types))
1.1183 - else:
1.1184 - items.append((param, set())) # Annotation has not succeeded.
1.1185 - params = params[1:]
1.1186 -
1.1187 - dstar_args = kw_args.items()
1.1188 -
1.1189 - # Construct temporary objects.
1.1190 -
1.1191 - if star_args:
1.1192 - star_invocation = self.make_star_args(invocation, subprogram, star_args)
1.1193 - self.dispatch(star_invocation)
1.1194 - star_types = star_invocation.types
1.1195 - else:
1.1196 - star_types = None
1.1197 -
1.1198 - if dstar_args:
1.1199 - dstar_invocation = self.make_dstar_args(invocation, subprogram, dstar_args)
1.1200 - self.dispatch(dstar_invocation)
1.1201 - dstar_types = dstar_invocation.types
1.1202 - else:
1.1203 - dstar_types = None
1.1204 -
1.1205 - # NOTE: Merge the objects properly.
1.1206 -
1.1207 - star_types = star_types or invocation.star and invocation.star.types
1.1208 - dstar_types = dstar_types or invocation.dstar and invocation.dstar.types
1.1209 -
1.1210 - # Add star and dstar.
1.1211 -
1.1212 - if star_types is not None:
1.1213 - if subprogram.star is not None:
1.1214 - param, default = subprogram.star
1.1215 - items.append((param, star_types))
1.1216 - else:
1.1217 - raise AnnotationMessage, "Invocation provides unwanted *args."
1.1218 - elif subprogram.star is not None:
1.1219 - param, default = subprogram.star
1.1220 - if not hasattr(default, "types"):
1.1221 - subprogram.star = param, self.dispatch(default) # NOTE: Review reprocessing.
1.1222 - items.append((param, default.types))
1.1223 -
1.1224 - if dstar_types is not None:
1.1225 - if subprogram.dstar is not None:
1.1226 - param, default = subprogram.dstar
1.1227 - items.append((param, dstar_types))
1.1228 - else:
1.1229 - raise AnnotationMessage, "Invocation provides unwanted **args."
1.1230 - elif subprogram.dstar is not None:
1.1231 - param, default = subprogram.dstar
1.1232 - if not hasattr(default, "types"):
1.1233 - subprogram.dstar = param, self.dispatch(default) # NOTE: Review reprocessing.
1.1234 - items.append((param, default.types))
1.1235 -
1.1236 - # Record the parameter types.
1.1237 -
1.1238 - self.annotate_parameters(subprogram, items)
1.1239 - return subprogram.paramtypes.items()
1.1240 -
1.1241 - def make_star_args(self, invocation, subprogram, star_args):
1.1242 -
1.1243 - "Make a subprogram which initialises a list containing 'star_args'."
1.1244 -
1.1245 - if not hasattr(invocation, "stars"):
1.1246 - invocation.stars = {}
1.1247 -
1.1248 - if not invocation.stars.has_key(subprogram.full_name()):
1.1249 - instance = getattr(invocation, "instance", None)
1.1250 -
1.1251 - code = [
1.1252 - Return(
1.1253 - instance=instance,
1.1254 - expr=MakeTuple(
1.1255 - instance=instance,
1.1256 - nodes=star_args
1.1257 - )
1.1258 - )
1.1259 - ]
1.1260 -
1.1261 - new_subprogram = Subprogram(
1.1262 - instance=instance,
1.1263 - name=None,
1.1264 - returns_value=1,
1.1265 - params=[],
1.1266 - star=None,
1.1267 - dstar=None,
1.1268 - code=code
1.1269 - )
1.1270 -
1.1271 - subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
1.1272 -
1.1273 - invocation.stars[subprogram.full_name()] = InvokeRef(
1.1274 - invocation.original,
1.1275 - instance=instance,
1.1276 - produces_result=1,
1.1277 - ref=new_subprogram
1.1278 - )
1.1279 -
1.1280 - return invocation.stars[subprogram.full_name()]
1.1281 -
1.1282 - def make_dstar_args(self, invocation, subprogram, dstar_args):
1.1283 -
1.1284 - """
1.1285 - Make a subprogram which initialises a dictionary built from the given
1.1286 - 'dstar_args'.
1.1287 - """
1.1288 -
1.1289 - if not hasattr(invocation, "dstars"):
1.1290 - invocation.dstars = {}
1.1291 -
1.1292 - if not invocation.dstars.has_key(subprogram.full_name()):
1.1293 - instance = getattr(invocation, "instance", None)
1.1294 -
1.1295 - code=[
1.1296 - StoreTemp(
1.1297 - instance=instance,
1.1298 - expr=InvokeFunction(
1.1299 - invocation.original,
1.1300 - instance=instance,
1.1301 - expr=LoadAttr(
1.1302 - instance=instance,
1.1303 - expr=LoadRef(
1.1304 - instance=instance,
1.1305 - ref=self.builtins
1.1306 - ),
1.1307 - name="dict",
1.1308 - nstype="module",
1.1309 - )
1.1310 - )
1.1311 - )
1.1312 - ]
1.1313 -
1.1314 - for arg, value in dstar_args:
1.1315 -
1.1316 - # NOTE: Constant not added to table.
1.1317 -
1.1318 - constant = Constant(name=repr(arg), value=arg)
1.1319 - code += [
1.1320 - StoreTemp(
1.1321 - instance=instance,
1.1322 - expr=InvokeFunction(
1.1323 - instance=instance,
1.1324 - expr=LoadName(
1.1325 - instance=instance,
1.1326 - name=constant.typename
1.1327 - )
1.1328 - ),
1.1329 - index="const"
1.1330 - ),
1.1331 - InvokeFunction(
1.1332 - invocation.original,
1.1333 - instance=instance,
1.1334 - expr=LoadAttr(
1.1335 - instance=instance,
1.1336 - expr=LoadTemp(
1.1337 - instance=instance
1.1338 - ),
1.1339 - name="__setitem__"
1.1340 - ),
1.1341 - args=[
1.1342 - LoadTemp(
1.1343 - instance=instance,
1.1344 - index="const",
1.1345 - release=1
1.1346 - ),
1.1347 - value
1.1348 - ]
1.1349 - )
1.1350 - ]
1.1351 -
1.1352 - code += [
1.1353 - Return(
1.1354 - instance=instance,
1.1355 - expr=LoadTemp(
1.1356 - instance=instance,
1.1357 - release=1
1.1358 - )
1.1359 - )
1.1360 - ]
1.1361 -
1.1362 - new_subprogram = Subprogram(
1.1363 - instance=instance,
1.1364 - name=None,
1.1365 - returns_value=1,
1.1366 - params=[],
1.1367 - star=None,
1.1368 - dstar=None,
1.1369 - code=code
1.1370 - )
1.1371 - subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
1.1372 -
1.1373 - invocation.dstars[subprogram.full_name()] = InvokeRef(
1.1374 - invocation.original,
1.1375 - instance=instance,
1.1376 - produces_result=1,
1.1377 - ref=new_subprogram
1.1378 - )
1.1379 -
1.1380 - return invocation.dstars[subprogram.full_name()]
1.1381 -
1.1382 -# Namespace-related abstractions.
1.1383 -
1.1384 -class Namespace:
1.1385 -
1.1386 - """
1.1387 - A local namespace which may either relate to a genuine set of function
1.1388 - locals or the initialisation of a structure or module.
1.1389 - """
1.1390 -
1.1391 - def __init__(self):
1.1392 -
1.1393 - """
1.1394 - Initialise the namespace with a mapping of local names to possible
1.1395 - types, a list of return values and of possible returned local
1.1396 - namespaces. The namespace also tracks the "current" types and a mapping
1.1397 - of temporary value names to types.
1.1398 - """
1.1399 -
1.1400 - self.names = {}
1.1401 - self.returns = set()
1.1402 - self.return_locals = set()
1.1403 - self.raises = set()
1.1404 - self.temp = {}
1.1405 - self.types = set()
1.1406 -
1.1407 - def set_types(self, types):
1.1408 -
1.1409 - "Set the current collection of 'types'."
1.1410 -
1.1411 - self.types = types.copy()
1.1412 -
1.1413 - def add(self, name, types):
1.1414 -
1.1415 - "Add to the entry with the given 'name' the specified 'types'."
1.1416 -
1.1417 - if self.names.has_key(name):
1.1418 - self.names[name].update(types)
1.1419 - else:
1.1420 - self.store(name, types)
1.1421 -
1.1422 - def store(self, name, types):
1.1423 -
1.1424 - "Store in (or associate with) the given 'name' the specified 'types'."
1.1425 -
1.1426 - self.names[name] = types.copy()
1.1427 -
1.1428 - __setitem__ = store
1.1429 -
1.1430 - def load(self, name):
1.1431 -
1.1432 - "Load the types associated with the given 'name'."
1.1433 -
1.1434 - return self.names[name]
1.1435 -
1.1436 - __getitem__ = load
1.1437 -
1.1438 - def has_key(self, name):
1.1439 - return self.names.has_key(name)
1.1440 -
1.1441 - def revoke(self, name, type):
1.1442 -
1.1443 - "Revoke from the entry for the given 'name' the specified 'type'."
1.1444 -
1.1445 - new_types = self.names[name].copy()
1.1446 - new_types.remove(type)
1.1447 - self.names[name] = new_types
1.1448 -
1.1449 - def revoke_exception_type(self, type):
1.1450 -
1.1451 - "Revoke the given 'type' from the collection of exception types."
1.1452 -
1.1453 - self.raises.remove(type)
1.1454 -
1.1455 - def revoke_temp_type(self, index, type):
1.1456 -
1.1457 - "Revoke from the temporary variable 'index' the given 'type'."
1.1458 -
1.1459 - new_types = self.temp[index][-1].copy()
1.1460 - new_types.remove(type)
1.1461 - self.temp[index][-1] = new_types
1.1462 -
1.1463 - def merge_namespace(self, namespace, everything=1):
1.1464 -
1.1465 - """
1.1466 - Merge items from the given 'namespace' with this namespace. When the
1.1467 - optional 'everything' parameter is set to a false value (unlike the
1.1468 - default), return values and locals snapshots will not be copied to this
1.1469 - namespace.
1.1470 - """
1.1471 -
1.1472 - self.merge_items(namespace.names.items())
1.1473 - self.raises.update(namespace.raises)
1.1474 - if everything:
1.1475 - self.returns.update(namespace.returns)
1.1476 - self.return_locals.update(namespace.return_locals)
1.1477 - for name, values in namespace.temp.items():
1.1478 - if values:
1.1479 - if not self.temp.has_key(name) or not self.temp[name]:
1.1480 - self.temp[name] = [set()]
1.1481 - self.temp[name][-1].update(values[-1])
1.1482 -
1.1483 - def merge_items(self, items):
1.1484 -
1.1485 - "Merge the given 'items' with this namespace."
1.1486 -
1.1487 - for name, types in items:
1.1488 - self.merge(name, types)
1.1489 -
1.1490 - def merge(self, name, types):
1.1491 -
1.1492 - "Merge the entry for the given 'name' and 'types' with this namespace."
1.1493 -
1.1494 - if not self.names.has_key(name):
1.1495 - self.names[name] = types.copy()
1.1496 - else:
1.1497 - existing = self.names[name]
1.1498 - existing.update(types)
1.1499 -
1.1500 - def snapshot(self):
1.1501 -
1.1502 - "Make a snapshot of the locals and remember them."
1.1503 -
1.1504 - namespace = Namespace()
1.1505 - namespace.merge_namespace(self)
1.1506 - self.return_locals.add(namespace)
1.1507 -
1.1508 - def reset(self):
1.1509 -
1.1510 - "Reset a namespace in preparation for merging with returned locals."
1.1511 -
1.1512 - self.names = {}
1.1513 -
1.1514 - def __repr__(self):
1.1515 - return repr(self.names) + " (temp) " + repr(self.temp)
1.1516 -
1.1517 -class Importer:
1.1518 -
1.1519 - "An import machine, searching for and loading modules."
1.1520 -
1.1521 - def __init__(self, path=None):
1.1522 -
1.1523 - """
1.1524 - Initialise the importer with the given search 'path' - a list of
1.1525 - directories to search for Python modules.
1.1526 - """
1.1527 -
1.1528 - self.path = path or [os.getcwd()]
1.1529 - self.path.append(libdir)
1.1530 - self.modules = {}
1.1531 -
1.1532 - def find_in_path(self, name):
1.1533 -
1.1534 - """
1.1535 - Find the given module 'name' in the search path, returning None where no
1.1536 - such module could be found, or a 2-tuple from the 'find' method
1.1537 - otherwise.
1.1538 - """
1.1539 -
1.1540 - for d in self.path:
1.1541 - m = self.find(d, name)
1.1542 - if m: return m
1.1543 - return None
1.1544 -
1.1545 - def find(self, d, name):
1.1546 -
1.1547 - """
1.1548 - In the directory 'd', find the given module 'name', where 'name' can
1.1549 - either refer to a single file module or to a package. Return None if the
1.1550 - 'name' cannot be associated with either a file or a package directory,
1.1551 - or a 2-tuple from '_find_package' or '_find_module' otherwise.
1.1552 - """
1.1553 -
1.1554 - m = self._find_package(d, name)
1.1555 - if m: return m
1.1556 - m = self._find_module(d, name)
1.1557 - if m: return m
1.1558 - return None
1.1559 -
1.1560 - def _find_module(self, d, name):
1.1561 -
1.1562 - """
1.1563 - In the directory 'd', find the given module 'name', returning None where
1.1564 - no suitable file exists in the directory, or a 2-tuple consisting of
1.1565 - None (indicating that no package directory is involved) and a filename
1.1566 - indicating the location of the module.
1.1567 - """
1.1568 -
1.1569 - name_py = name + os.extsep + "py"
1.1570 - filename = self._find_file(d, name_py)
1.1571 - if filename:
1.1572 - return None, filename
1.1573 - return None
1.1574 -
1.1575 - def _find_package(self, d, name):
1.1576 -
1.1577 - """
1.1578 - In the directory 'd', find the given package 'name', returning None
1.1579 - where no suitable package directory exists, or a 2-tuple consisting of
1.1580 - a directory (indicating the location of the package directory itself)
1.1581 - and a filename indicating the location of the __init__.py module which
1.1582 - declares the package's top-level contents.
1.1583 - """
1.1584 -
1.1585 - filename = self._find_file(d, name)
1.1586 - if filename:
1.1587 - init_py = "__init__" + os.path.extsep + "py"
1.1588 - init_py_filename = self._find_file(filename, init_py)
1.1589 - if init_py_filename:
1.1590 - return filename, init_py_filename
1.1591 - return None
1.1592 -
1.1593 - def _find_file(self, d, filename):
1.1594 -
1.1595 - """
1.1596 - Return the filename obtained when searching the directory 'd' for the
1.1597 - given 'filename', or None if no actual file exists for the filename.
1.1598 - """
1.1599 -
1.1600 - filename = os.path.join(d, filename)
1.1601 - if os.path.exists(filename):
1.1602 - return filename
1.1603 - else:
1.1604 - return None
1.1605 -
1.1606 - def load(self, name, builtins, alias=None):
1.1607 -
1.1608 - """
1.1609 - Load the module or package with the given 'name' and using the specified
1.1610 - 'builtins'. Return an Attribute object referencing the loaded module or
1.1611 - package, or None if no such module or package exists.
1.1612 - """
1.1613 -
1.1614 - if self.modules.has_key(name):
1.1615 - return Attribute(None, self.modules[name])
1.1616 -
1.1617 - path = name.split(".")
1.1618 - m = self.find_in_path(path[0])
1.1619 - if not m:
1.1620 - return None # NOTE: Import error.
1.1621 - d, filename = m
1.1622 -
1.1623 - if self.modules.has_key(path[0]):
1.1624 - top = module = self.modules[path[0]]
1.1625 - else:
1.1626 - top = module = self.modules[path[0]] = load(filename, builtins, path[0], self, no_annotate=1)
1.1627 - annotate(module, builtins, self)
1.1628 -
1.1629 - if len(path) > 1:
1.1630 - path_so_far = path[:1]
1.1631 - for p in path[1:]:
1.1632 - path_so_far.append(p)
1.1633 - m = self.find(d, p)
1.1634 - if not m:
1.1635 - return None # NOTE: Import error.
1.1636 - d, filename = m
1.1637 - module_name = ".".join(path_so_far)
1.1638 -
1.1639 - if self.modules.has_key(module_name):
1.1640 - submodule = self.modules[module_name]
1.1641 - else:
1.1642 - submodule = self.modules[module_name] = load(filename, builtins, module_name, self, no_annotate=1)
1.1643 - annotate(submodule, builtins, self)
1.1644 -
1.1645 - # Store the submodule within its parent module.
1.1646 -
1.1647 - module.namespace[p] = [Attribute(None, submodule)]
1.1648 - module = submodule
1.1649 -
1.1650 - if alias:
1.1651 - return Attribute(None, module)
1.1652 - else:
1.1653 - return Attribute(None, top)
1.1654 -
1.1655 -def combine(target, additions):
1.1656 -
1.1657 - """
1.1658 - Merge into the 'target' sequence the given 'additions', preventing duplicate
1.1659 - items.
1.1660 - """
1.1661 -
1.1662 - for addition in additions:
1.1663 - if addition not in target:
1.1664 - target.append(addition)
1.1665 -
1.1666 -def find_attributes(structure, name):
1.1667 -
1.1668 - """
1.1669 - Find for the given 'structure' all attributes for the given 'name', visiting
1.1670 - base classes where appropriate and returning the attributes in order of
1.1671 - descending precedence for all possible base classes.
1.1672 -
1.1673 - The elements in the result list are 2-tuples which contain the attribute and
1.1674 - the structure involved in accessing the attribute.
1.1675 - """
1.1676 -
1.1677 - # First attempt to search the instance/class namespace.
1.1678 -
1.1679 - try:
1.1680 - l = structure.namespace.load(name)
1.1681 - attributes = []
1.1682 - for attribute in l:
1.1683 - attributes.append((attribute, structure))
1.1684 -
1.1685 - # If that does not work, attempt to investigate any class or base classes.
1.1686 -
1.1687 - except KeyError:
1.1688 - attributes = []
1.1689 -
1.1690 - # Investigate any instance's implementing class.
1.1691 -
1.1692 - if isinstance(structure, Instance):
1.1693 - for attr in structure.namespace.load("__class__"):
1.1694 - cls = attr.type
1.1695 - l = get_attributes(cls, name)
1.1696 - combine(attributes, l)
1.1697 -
1.1698 - # Investigate any class's base classes.
1.1699 -
1.1700 - elif isinstance(structure, Class):
1.1701 -
1.1702 - # If no base classes exist, return an indicator that no attribute
1.1703 - # exists.
1.1704 -
1.1705 - if not structure.base_refs:
1.1706 - return [(None, structure)]
1.1707 -
1.1708 - # Otherwise, find all possible base classes.
1.1709 -
1.1710 - for base_refs in structure.base_refs:
1.1711 - base_attributes = []
1.1712 -
1.1713 - # For each base class, find attributes either in the base
1.1714 - # class or its own base classes.
1.1715 -
1.1716 - for base_ref in base_refs:
1.1717 - l = get_attributes(base_ref, name)
1.1718 - combine(base_attributes, l)
1.1719 -
1.1720 - combine(attributes, base_attributes)
1.1721 -
1.1722 - return attributes
1.1723 -
1.1724 -def get_attributes(structure, name):
1.1725 -
1.1726 - """
1.1727 - Return all possible attributes for the given 'structure' having the given
1.1728 - 'name', wrapping each attribute in an Attribute object which includes
1.1729 - context information for the attribute access.
1.1730 -
1.1731 - The elements in the result list are 2-tuples which contain the attribute and
1.1732 - the structure involved in accessing the attribute.
1.1733 - """
1.1734 -
1.1735 - if isinstance(structure, Attribute):
1.1736 - structure = structure.type
1.1737 - results = []
1.1738 - for attribute, accessor in find_attributes(structure, name):
1.1739 -
1.1740 - # Detect class attribute access via instances.
1.1741 -
1.1742 - if attribute is not None and isinstance(structure, Instance) and isinstance(accessor, Class):
1.1743 - attribute = accessor.get_attribute_for_instance(attribute, structure)
1.1744 -
1.1745 - # Produce an attribute with the appropriate context.
1.1746 -
1.1747 - if attribute is not None and isinstance(structure, Structure):
1.1748 - results.append((Attribute(structure, attribute.type), accessor))
1.1749 - else:
1.1750 - results.append((attribute, accessor))
1.1751 -
1.1752 - return results
1.1753 -
1.1754 -def prompt(vars):
1.1755 - try:
1.1756 - while 1:
1.1757 - s = raw_input("> ")
1.1758 - print eval(s, vars)
1.1759 - except EOFError:
1.1760 - pass
1.1761 -
1.1762 -# Convenience functions.
1.1763 -
1.1764 -def load(name, builtins=None, module_name=None, importer=None, no_annotate=0):
1.1765 -
1.1766 - """
1.1767 - Load the module with the given 'name' (which may be a full module path),
1.1768 - using the optional 'builtins' to resolve built-in names, and using the
1.1769 - optional 'importer' to provide a means of finding and loading modules.
1.1770 - """
1.1771 -
1.1772 - module = simplify.simplify(name, builtins is None, module_name)
1.1773 - fixnames.fix(module, builtins)
1.1774 - if not no_annotate:
1.1775 - annotate(module, builtins, importer)
1.1776 - return module
1.1777 -
1.1778 -def annotate(module, builtins=None, importer=None):
1.1779 -
1.1780 - """
1.1781 - Annotate the given 'module', also employing the optional 'builtins' module,
1.1782 - if specified. If the optional 'importer' is given, use that to find and load
1.1783 - modules.
1.1784 - """
1.1785 -
1.1786 - annotator = Annotator(importer)
1.1787 - if builtins is not None:
1.1788 - annotator.process(module, builtins)
1.1789 - else:
1.1790 - annotator.process(module)
1.1791 -
1.1792 -# vim: tabstop=4 expandtab shiftwidth=4
2.1 --- a/fixnames.py Sun May 27 18:19:01 2007 +0200
2.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2.3 @@ -1,489 +0,0 @@
2.4 -#!/usr/bin/env python
2.5 -
2.6 -"""
2.7 -Fix name-related operations. The code in this module operates upon simplified
2.8 -program node trees.
2.9 -
2.10 -Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk>
2.11 -
2.12 -This software is free software; you can redistribute it and/or
2.13 -modify it under the terms of the GNU General Public License as
2.14 -published by the Free Software Foundation; either version 2 of
2.15 -the License, or (at your option) any later version.
2.16 -
2.17 -This software is distributed in the hope that it will be useful,
2.18 -but WITHOUT ANY WARRANTY; without even the implied warranty of
2.19 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2.20 -GNU General Public License for more details.
2.21 -
2.22 -You should have received a copy of the GNU General Public
2.23 -License along with this library; see the file LICENCE.txt
2.24 -If not, write to the Free Software Foundation, Inc.,
2.25 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
2.26 -
2.27 ---------
2.28 -
2.29 -To use this module, the easiest approach is to use the fix function:
2.30 -
2.31 -fix(module)
2.32 -
2.33 -The more complicated approach involves instantiating a Fixer object:
2.34 -
2.35 -fixer = Fixer()
2.36 -
2.37 -Then, applying the fixer to an existing module:
2.38 -
2.39 -fixer.process(module)
2.40 -
2.41 -If a module containing built-in classes and functions exists, apply the fixer as
2.42 -follows:
2.43 -
2.44 -fixer.process(module, builtins)
2.45 -"""
2.46 -
2.47 -from simplified import *
2.48 -
2.49 -# Fixing of name-related operations.
2.50 -
2.51 -class Fixer(Visitor):
2.52 -
2.53 - """
2.54 - The name fixer which traverses the program nodes in a module, typically
2.55 - depth-first, and maintains a record of name usage in the different
2.56 - namespaces. As a consequence of various observations, some parts of the
2.57 - program node tree are modified with different operations employed to those
2.58 - originally defined.
2.59 -
2.60 - There are two kinds of subprograms in modules: functions/methods and
2.61 - internal subprograms which support things like loops. The latter kind of
2.62 - subprogram may acquire the locals from their callers and must therefore be
2.63 - traversed with information from such callers. Thus, we choose the top-level
2.64 - code and all functions/methods as roots for processing, following
2.65 - invocations of internal subprograms in order to reach all subprograms that
2.66 - are defined in each module.
2.67 -
2.68 - top-level
2.69 - ...
2.70 - invoke function
2.71 - ...
2.72 - invoke loop -> subprogram (internal)
2.73 - ...
2.74 -
2.75 - subprogram (function)
2.76 - ...
2.77 - invoke loop -> subprogram (internal)
2.78 - ...
2.79 -
2.80 - ...
2.81 -
2.82 - The above approach should guarantee that all subprograms are traversed and
2.83 - that all name lookups are correctly categorised.
2.84 - """
2.85 -
2.86 - def __init__(self):
2.87 -
2.88 - "Initialise the name fixer."
2.89 -
2.90 - Visitor.__init__(self)
2.91 -
2.92 - # Satisfy visitor issues.
2.93 -
2.94 - self.visitor = self
2.95 -
2.96 - def process(self, module, builtins=None):
2.97 -
2.98 - """
2.99 - Process the given 'module' optionally using some 'builtins' to reference
2.100 - built-in objects.
2.101 - """
2.102 -
2.103 - # The fixer maintains a list of transformed subprograms (added for each
2.104 - # of the processing "roots" and also for each invoked internal
2.105 - # subprogram), along with a list of current subprograms (used to avoid
2.106 - # recursion issues) and a list of current namespaces (used to recall
2.107 - # namespaces upon invoking internal subprograms).
2.108 -
2.109 - self.subprograms = []
2.110 - self.current_subprograms = []
2.111 - self.current_namespaces = []
2.112 -
2.113 - # First, process the top-level code, finding out which names are
2.114 - # defined at that level.
2.115 -
2.116 - self.global_namespace = None
2.117 - self.module = module
2.118 - self.builtins = builtins or module
2.119 -
2.120 - self.process_node(self.module)
2.121 -
2.122 - # Then, process all functions and methods, providing a global namespace.
2.123 - # By setting a global namespace, we influence the resolution of names:
2.124 - # those which are global to the top-level module (processed above) are
2.125 - # considered as built-in names, whereas those which are global to a
2.126 - # function or method are searched for in the global namespace.
2.127 -
2.128 - self.global_namespace = self.namespace
2.129 -
2.130 - for subprogram in self.module.simplifier.subprograms:
2.131 -
2.132 - # Internal subprograms are skipped here and processed specially via
2.133 - # Invoke nodes.
2.134 -
2.135 - if not getattr(subprogram, "internal", 0):
2.136 - self.subprograms.append(self.process_node(subprogram))
2.137 -
2.138 - # Ultimately, we redefine the list of subprograms on the visitor.
2.139 -
2.140 - self.module.simplifier.subprograms = self.subprograms
2.141 - return self.module
2.142 -
2.143 - def process_node(self, node, namespace=None):
2.144 -
2.145 - """
2.146 - Process a subprogram or module 'node', discovering from attributes on
2.147 - 'node' any initial locals. Return a modified subprogram or module.
2.148 - """
2.149 -
2.150 - # Do not process subprograms already being processed.
2.151 -
2.152 - if node in self.current_subprograms:
2.153 - return None
2.154 -
2.155 - # Obtain a namespace either based on locals or on a structure.
2.156 -
2.157 - structure = structure=getattr(node, "structure", None)
2.158 -
2.159 - # If passed some namespace, use that as the current namespace.
2.160 -
2.161 - if namespace is not None:
2.162 - self.namespace.merge_namespace(namespace)
2.163 - else:
2.164 - self.namespace = NameOrganiser(structure)
2.165 -
2.166 - # Record the current subprogram and namespace.
2.167 -
2.168 - self.current_subprograms.append(node)
2.169 - self.current_namespaces.append(self.namespace)
2.170 -
2.171 - # NOTE: Avoid PEP 227 (nested scopes) whilst permitting references to a
2.172 - # NOTE: subprogram within itself. Do not define the name of the function
2.173 - # NOTE: within a method definition.
2.174 -
2.175 - if isinstance(node, Subprogram) and getattr(node, "name", None) is not None and not getattr(node, "is_method", 0):
2.176 - self.namespace.store(node.name)
2.177 -
2.178 - # Register the names of parameters in the namespace.
2.179 -
2.180 - if hasattr(node, "params"):
2.181 - new_params = []
2.182 - for param, default in node.params:
2.183 - new_params.append((param, self.dispatch(default)))
2.184 - self.namespace.store(param)
2.185 - node.params = new_params
2.186 - if getattr(node, "star", None):
2.187 - param, default = node.star
2.188 - self.namespace.store(param)
2.189 - node.star = param, self.dispatch(default)
2.190 - if getattr(node, "dstar", None):
2.191 - param, default = node.dstar
2.192 - self.namespace.store(param)
2.193 - node.dstar = param, self.dispatch(default)
2.194 -
2.195 - # Add namespace details to any structure involved.
2.196 -
2.197 - if hasattr(node, "structure") and node.structure is not None:
2.198 -
2.199 - # Initialise bases where appropriate.
2.200 -
2.201 - if hasattr(node.structure, "bases"):
2.202 - bases = []
2.203 - for base in node.structure.bases:
2.204 - bases.append(self.dispatch(base))
2.205 - node.structure.bases = bases
2.206 -
2.207 - # Dispatch to the code itself.
2.208 -
2.209 - result = self.dispatch(node)
2.210 - result.organiser = self.namespace
2.211 -
2.212 - # Restore the previous subprogram and namespace.
2.213 -
2.214 - self.current_namespaces.pop()
2.215 - if self.current_namespaces:
2.216 - self.namespace = self.current_namespaces[-1]
2.217 - self.current_subprograms.pop()
2.218 -
2.219 - return result
2.220 -
2.221 - # Visitor methods.
2.222 -
2.223 - def default(self, node):
2.224 -
2.225 - """
2.226 - Process the given 'node', given that it does not have a specific
2.227 - handler.
2.228 - """
2.229 -
2.230 - for attr in ("pos_args",):
2.231 - value = getattr(node, attr, None)
2.232 - if value is not None:
2.233 - setattr(node, attr, self.dispatches(value))
2.234 - for attr in ("kw_args",):
2.235 - value = getattr(node, attr, None)
2.236 - if value is not None:
2.237 - setattr(node, attr, self.dispatch_dict(value))
2.238 - for attr in ("expr", "lvalue", "test", "star", "dstar"):
2.239 - value = getattr(node, attr, None)
2.240 - if value is not None:
2.241 - setattr(node, attr, self.dispatch(value))
2.242 - for attr in ("body", "else_", "handler", "finally_", "code", "choices", "nodes"):
2.243 - value = getattr(node, attr, None)
2.244 - if value is not None:
2.245 - setattr(node, attr, self.dispatches(value))
2.246 - return node
2.247 -
2.248 - def dispatch(self, node, *args):
2.249 - return Visitor.dispatch(self, node, *args)
2.250 -
2.251 - def visitGlobal(self, global_):
2.252 - for name in global_.names:
2.253 - self.namespace.make_global(name)
2.254 - return global_
2.255 -
2.256 - def visitLoadName(self, loadname):
2.257 -
2.258 - "Transform the 'loadname' node to a specific, scope-sensitive node."
2.259 -
2.260 - scope = self.namespace.find_for_load(loadname.name)
2.261 -
2.262 - # For structure namespaces, load an attribute.
2.263 -
2.264 - if scope == "structure":
2.265 - result = self.dispatch(
2.266 - LoadAttr(loadname.original, loadname.defining,
2.267 - expr=LoadRef(loadname.original,
2.268 - ref=self.namespace.structure),
2.269 - name=loadname.name,
2.270 - nstype="structure")
2.271 - )
2.272 -
2.273 - # For global accesses (ie. those outside the local namespace)...
2.274 -
2.275 - elif scope == "global":
2.276 -
2.277 - # Where a distinct global namespace exists, examine it.
2.278 -
2.279 - if self.global_namespace is not None:
2.280 - scope = self.global_namespace.find_for_load(loadname.name)
2.281 -
2.282 - # Where the name is outside the global namespace, it must be a
2.283 - # built-in.
2.284 -
2.285 - if scope == "global":
2.286 - result = self.dispatch(
2.287 - LoadAttr(loadname.original, loadname.defining,
2.288 - expr=LoadRef(loadname.original,
2.289 - ref=self.builtins),
2.290 - name=loadname.name,
2.291 - nstype="module")
2.292 - )
2.293 -
2.294 - # Otherwise, it is within the global namespace and must be a
2.295 - # global.
2.296 -
2.297 - else:
2.298 - result = self.dispatch(
2.299 - LoadAttr(loadname.original, loadname.defining,
2.300 - expr=LoadRef(loadname.original,
2.301 - ref=self.module),
2.302 - name=loadname.name,
2.303 - nstype="module")
2.304 - )
2.305 -
2.306 - # Where no global namespace exists, we are at the module level and
2.307 - # must be accessing a built-in.
2.308 -
2.309 - else:
2.310 - result = self.dispatch(
2.311 - LoadAttr(loadname.original, loadname.defining,
2.312 - expr=LoadRef(loadname.original,
2.313 - ref=self.builtins),
2.314 - name=loadname.name,
2.315 - nstype="module")
2.316 - )
2.317 -
2.318 - # For local accesses...
2.319 -
2.320 - else:
2.321 -
2.322 - # Where a distinct global namespace exists, it must be a local.
2.323 -
2.324 - if self.global_namespace is not None:
2.325 - result = loadname
2.326 -
2.327 - # Otherwise, we must be accessing a global (which is local at the
2.328 - # module level).
2.329 -
2.330 - else:
2.331 - result = self.dispatch(
2.332 - LoadAttr(loadname.original, loadname.defining,
2.333 - expr=LoadRef(loadname.original,
2.334 - ref=self.module),
2.335 - name=loadname.name,
2.336 - nstype="module")
2.337 - )
2.338 -
2.339 - return result
2.340 -
2.341 - def visitStoreName(self, storename):
2.342 -
2.343 - "Transform the 'storename' node to a specific, scope-sensitive node."
2.344 -
2.345 - scope = self.namespace.find_for_store(storename.name)
2.346 -
2.347 - # For structure namespaces, store an attribute.
2.348 -
2.349 - if scope == "structure":
2.350 - self.namespace.store(storename.name)
2.351 -
2.352 - return self.dispatch(
2.353 - StoreAttr(storename.original, storename.defining,
2.354 - lvalue=LoadRef(storename.original,
2.355 - ref=self.namespace.structure),
2.356 - name=storename.name,
2.357 - expr=storename.expr,
2.358 - nstype="structure")
2.359 - )
2.360 -
2.361 - # Where the name is outside the local namespace, disallow any built-in
2.362 - # assignment and store the name globally.
2.363 -
2.364 - elif scope == "global":
2.365 - return self.dispatch(
2.366 - StoreAttr(storename.original, storename.defining,
2.367 - lvalue=LoadRef(storename.original,
2.368 - ref=self.module),
2.369 - name=storename.name,
2.370 - expr=storename.expr,
2.371 - nstype="module")
2.372 - )
2.373 -
2.374 - # For local namespace accesses...
2.375 -
2.376 - else:
2.377 - self.namespace.store(storename.name)
2.378 -
2.379 - # If a distinct global namespace exists, it must be a local access.
2.380 -
2.381 - if self.global_namespace is not None:
2.382 - return storename
2.383 -
2.384 - # Otherwise, the name is being set at the module level and is
2.385 - # considered global.
2.386 -
2.387 - else:
2.388 - return self.dispatch(
2.389 - StoreAttr(storename.original, storename.defining,
2.390 - lvalue=LoadRef(storename.original,
2.391 - ref=self.module),
2.392 - name=storename.name,
2.393 - expr=storename.expr,
2.394 - nstype="module")
2.395 - )
2.396 -
2.397 - def visitInvokeFunction(self, invoke):
2.398 -
2.399 - "Transform the 'invoke' node, performing processing on subprograms."
2.400 -
2.401 - return self.default(invoke)
2.402 -
2.403 - def visitInvokeRef(self, invoke):
2.404 -
2.405 - "Transform the 'invoke' node, performing processing on subprograms."
2.406 -
2.407 - # The special case of internal subprogram invocation is addressed by
2.408 - # propagating namespace information to the subprogram and processing it.
2.409 -
2.410 - if invoke.share_locals:
2.411 - subprogram = self.process_node(invoke.ref, self.namespace)
2.412 - else:
2.413 - subprogram = self.process_node(invoke.ref)
2.414 -
2.415 - if subprogram is not None:
2.416 - self.subprograms.append(subprogram)
2.417 - return invoke
2.418 -
2.419 -class ScopeMismatch(Exception):
2.420 - pass
2.421 -
2.422 -class NameOrganiser:
2.423 -
2.424 - """
2.425 - A local namespace which may either relate to a genuine set of function
2.426 - locals or the initialisation of a structure.
2.427 - """
2.428 -
2.429 - def __init__(self, structure=None):
2.430 -
2.431 - "Initialise the namespace with an optional 'structure'."
2.432 -
2.433 - self.structure = structure
2.434 - if structure is not None:
2.435 - self.local = "structure"
2.436 - else:
2.437 - self.local = "local"
2.438 -
2.439 - # Names may be self.local or "global".
2.440 -
2.441 - self.names = {}
2.442 -
2.443 - def make_global(self, name):
2.444 - if not self.names.has_key(name):
2.445 - self.names[name] = "global"
2.446 - elif self.names[name] == self.local:
2.447 - raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local)
2.448 -
2.449 - def find_for_load(self, name):
2.450 - return self.names.get(name, "global")
2.451 -
2.452 - def find_for_store(self, name):
2.453 - return self.names.get(name, self.local)
2.454 -
2.455 - def store(self, name):
2.456 - if self.names.get(name) != "global":
2.457 - self.names[name] = self.local
2.458 - else:
2.459 - raise ScopeMismatch, "Name '%s' already considered as global." % name
2.460 -
2.461 - def merge(self, name, scope):
2.462 - if self.names.get(name) in (None, scope):
2.463 - self.names[name] = scope
2.464 - else:
2.465 - raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.names[name])
2.466 -
2.467 - def merge_namespace(self, namespace):
2.468 - self.merge_items(namespace.names.items())
2.469 -
2.470 - def merge_items(self, items):
2.471 - for name, scope in items:
2.472 - self.merge(name, scope)
2.473 -
2.474 - def __repr__(self):
2.475 - return repr(self.names)
2.476 -
2.477 -# Convenience functions.
2.478 -
2.479 -def fix(module, builtins=None):
2.480 -
2.481 - """
2.482 - Fix the names in the given 'module', also employing the optional 'builtins'
2.483 - module, if specified.
2.484 - """
2.485 -
2.486 - fixer = Fixer()
2.487 - if builtins is not None:
2.488 - fixer.process(module, builtins)
2.489 - else:
2.490 - fixer.process(module)
2.491 -
2.492 -# vim: tabstop=4 expandtab shiftwidth=4
3.1 --- a/simplified/__init__.py Sun May 27 18:19:01 2007 +0200
3.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
3.3 @@ -1,37 +0,0 @@
3.4 -#!/usr/bin/env python
3.5 -
3.6 -"""
3.7 -Simplified program representation.
3.8 -
3.9 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
3.10 -
3.11 -This software is free software; you can redistribute it and/or
3.12 -modify it under the terms of the GNU General Public License as
3.13 -published by the Free Software Foundation; either version 2 of
3.14 -the License, or (at your option) any later version.
3.15 -
3.16 -This software is distributed in the hope that it will be useful,
3.17 -but WITHOUT ANY WARRANTY; without even the implied warranty of
3.18 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.19 -GNU General Public License for more details.
3.20 -
3.21 -You should have received a copy of the GNU General Public
3.22 -License along with this library; see the file LICENCE.txt
3.23 -If not, write to the Free Software Foundation, Inc.,
3.24 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
3.25 -"""
3.26 -
3.27 -__version__ = "0.1"
3.28 -
3.29 -from simplified.ast import *
3.30 -from simplified.data import *
3.31 -from simplified.program import *
3.32 -from simplified.utils import *
3.33 -import os
3.34 -
3.35 -# Location of the built-in libraries.
3.36 -# NOTE: Change this if the package structure changes.
3.37 -
3.38 -libdir = os.path.join(os.path.split(os.path.split(__file__)[0])[0], "lib")
3.39 -
3.40 -# vim: tabstop=4 expandtab shiftwidth=4
4.1 --- a/simplified/ast.py Sun May 27 18:19:01 2007 +0200
4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
4.3 @@ -1,44 +0,0 @@
4.4 -#!/usr/bin/env python
4.5 -
4.6 -"""
4.7 -Additional program AST nodes.
4.8 -
4.9 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
4.10 -
4.11 -This software is free software; you can redistribute it and/or
4.12 -modify it under the terms of the GNU General Public License as
4.13 -published by the Free Software Foundation; either version 2 of
4.14 -the License, or (at your option) any later version.
4.15 -
4.16 -This software is distributed in the hope that it will be useful,
4.17 -but WITHOUT ANY WARRANTY; without even the implied warranty of
4.18 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.19 -GNU General Public License for more details.
4.20 -
4.21 -You should have received a copy of the GNU General Public
4.22 -License along with this library; see the file LICENCE.txt
4.23 -If not, write to the Free Software Foundation, Inc.,
4.24 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
4.25 -"""
4.26 -
4.27 -class Self:
4.28 -
4.29 - """
4.30 - A program node encapsulating object/context information in an argument list.
4.31 - This is not particularly like Attribute, Class, Instance or other such
4.32 - things, since it actually appears in the program representation.
4.33 - """
4.34 -
4.35 - def __init__(self, attribute):
4.36 - self.types = set()
4.37 - self.types.add(attribute)
4.38 -
4.39 -class Op:
4.40 -
4.41 - "A replacement AST node representing an operation in a Compare construct."
4.42 -
4.43 - def __init__(self, name, expr):
4.44 - self.name = name
4.45 - self.expr = expr
4.46 -
4.47 -# vim: tabstop=4 expandtab shiftwidth=4
5.1 --- a/simplified/data.py Sun May 27 18:19:01 2007 +0200
5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
5.3 @@ -1,237 +0,0 @@
5.4 -#!/usr/bin/env python
5.5 -
5.6 -"""
5.7 -Simplified program data.
5.8 -
5.9 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
5.10 -
5.11 -This software is free software; you can redistribute it and/or
5.12 -modify it under the terms of the GNU General Public License as
5.13 -published by the Free Software Foundation; either version 2 of
5.14 -the License, or (at your option) any later version.
5.15 -
5.16 -This software is distributed in the hope that it will be useful,
5.17 -but WITHOUT ANY WARRANTY; without even the implied warranty of
5.18 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5.19 -GNU General Public License for more details.
5.20 -
5.21 -You should have received a copy of the GNU General Public
5.22 -License along with this library; see the file LICENCE.txt
5.23 -If not, write to the Free Software Foundation, Inc.,
5.24 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
5.25 -"""
5.26 -
5.27 -from simplified.utils import Structure, WithName, name
5.28 -
5.29 -# Special non-program nodes.
5.30 -
5.31 -class _Class(Structure, WithName):
5.32 -
5.33 - "A Python class."
5.34 -
5.35 - def __init__(self, *args, **kw):
5.36 - Structure.__init__(self, *args, **kw)
5.37 - WithName.__init__(self)
5.38 -
5.39 - def full_name(self):
5.40 - return "class %s" % self._full_name
5.41 -
5.42 -class SingleInstanceClass(_Class):
5.43 -
5.44 - "A Python class."
5.45 -
5.46 - def __init__(self, *args, **kw):
5.47 - _Class.__init__(self, *args, **kw)
5.48 - self.instance = None
5.49 -
5.50 - def has_instance(self, node):
5.51 - return self.instance is not None
5.52 -
5.53 - def add_instance(self, node, instance):
5.54 - self.instance = instance
5.55 -
5.56 - def get_instance(self, node):
5.57 - return self.instance
5.58 -
5.59 - def get_instance_name(self, instance):
5.60 - return self._full_name
5.61 -
5.62 - # Attribute propagation.
5.63 -
5.64 - def get_attribute_for_instance(self, attribute, instance):
5.65 - return attribute
5.66 -
5.67 -class MultipleInstanceClass(_Class):
5.68 -
5.69 - "A Python class."
5.70 -
5.71 - def __init__(self, *args, **kw):
5.72 - _Class.__init__(self, *args, **kw)
5.73 - self.instances = {}
5.74 - self.attributes_for_instances = {}
5.75 -
5.76 - def _get_key(self, node):
5.77 - return id(getattr(node, "original", None)) # self.module.original
5.78 -
5.79 - def has_instance(self, node):
5.80 - return self.instances.has_key(self._get_key(node))
5.81 -
5.82 - def add_instance(self, node, instance):
5.83 - self.instances[self._get_key(node)] = instance
5.84 -
5.85 - def get_instance(self, node):
5.86 - return self.instances[self._get_key(node)]
5.87 -
5.88 - def get_instance_name(self, instance):
5.89 - return name(instance, self._full_name)
5.90 -
5.91 - # Attribute propagation.
5.92 -
5.93 - def get_attribute_for_instance(self, attribute, instance):
5.94 -
5.95 - # Create specialised methods.
5.96 -
5.97 - if isinstance(attribute.type, Subprogram):
5.98 - subprogram = attribute.type
5.99 -
5.100 - # Each instance may have its own version of the subprogram.
5.101 -
5.102 - key = (subprogram, instance)
5.103 - if not self.attributes_for_instances.has_key(key):
5.104 - new_subprogram = subprogram.copy(instance, subprogram.full_name())
5.105 - subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
5.106 - self.attributes_for_instances[key] = Attribute(attribute.context, new_subprogram)
5.107 - print "New subprogram", new_subprogram, "for", key
5.108 -
5.109 - return self.attributes_for_instances[key]
5.110 -
5.111 - # The original nodes are returned for other attributes.
5.112 -
5.113 - else:
5.114 - return attribute
5.115 -
5.116 -class SelectiveMultipleInstanceClass(MultipleInstanceClass):
5.117 -
5.118 - "A Python class which provides multiple instances depending on the class."
5.119 -
5.120 - def _get_key(self, node):
5.121 - if self.namespace.has_key("__atomic__"):
5.122 - return id(self)
5.123 - else:
5.124 - return MultipleInstanceClass._get_key(self, node)
5.125 -
5.126 -class ProlificMultipleInstanceClass(MultipleInstanceClass):
5.127 -
5.128 - """
5.129 - A Python class which provides multiple instances for different versions of
5.130 - methods. In order to avoid unbounded instance production (since new
5.131 - instances cause new copies of methods which in turn would cause new
5.132 - instances), a relations dictionary is maintained which attempts to map
5.133 - "requesting instances" to existing instances, suggesting such instances in
5.134 - preference to new ones.
5.135 - """
5.136 -
5.137 - def __init__(self, *args, **kw):
5.138 - MultipleInstanceClass.__init__(self, *args, **kw)
5.139 - self.instance_relations = {}
5.140 -
5.141 - def _get_key(self, node):
5.142 - if self.namespace.has_key("__atomic__"):
5.143 - return id(self)
5.144 - else:
5.145 - return id(node)
5.146 -
5.147 - def has_instance(self, node):
5.148 - requesting_instance = getattr(node, "instance", None)
5.149 - #return requesting_instance is not None and requesting_instance.get_class() is self or \
5.150 - return self.instance_relations.has_key(requesting_instance) or self.instances.has_key(self._get_key(node))
5.151 -
5.152 - def add_instance(self, node, instance):
5.153 - requesting_instance = getattr(node, "instance", None)
5.154 - print "New instance", instance, "for", id(node), requesting_instance
5.155 - self.instances[self._get_key(node)] = instance
5.156 - if requesting_instance is not None:
5.157 - self.instance_relations[requesting_instance] = instance
5.158 - requesting_instance.get_class().instance_relations[instance] = requesting_instance
5.159 -
5.160 - def get_instance(self, node):
5.161 - requesting_instance = getattr(node, "instance", None)
5.162 - #if requesting_instance is not None and requesting_instance.get_class() is self:
5.163 - # return requesting_instance
5.164 - return self.instance_relations.get(requesting_instance) or self.instances[self._get_key(node)]
5.165 -
5.166 -class Instance(Structure):
5.167 -
5.168 - "An instance."
5.169 -
5.170 - def full_name(self):
5.171 - return self.get_class().get_instance_name(self)
5.172 -
5.173 - def get_class(self):
5.174 - for n in self.namespace.load("__class__"):
5.175 - return n.type
5.176 - else:
5.177 - raise ValueError, "__class__"
5.178 -
5.179 - def __repr__(self):
5.180 - return "Instance of type '%s'" % self.full_name()
5.181 -
5.182 - def __eq__(self, other):
5.183 - # NOTE: Single instance: all instances are the same
5.184 - # NOTE: Multiple instances: all instances are different
5.185 - return self.full_name() == other.full_name()
5.186 -
5.187 - def __hash__(self):
5.188 - return id(self)
5.189 -
5.190 -class Constant:
5.191 -
5.192 - "A constant initialised with a type name for future processing."
5.193 -
5.194 - def __init__(self, name, value):
5.195 - self.name = name
5.196 - self.value = value
5.197 - self.typename = self.value.__class__.__name__
5.198 -
5.199 -class Attribute:
5.200 -
5.201 - """
5.202 - An attribute abstraction, indicating the type of the attribute along with
5.203 - its context or origin.
5.204 - """
5.205 -
5.206 - def __init__(self, context, type):
5.207 - self.context = context
5.208 - self.type = type
5.209 -
5.210 - def __eq__(self, other):
5.211 - return hasattr(other, "type") and other.type == self.type or other == self.type
5.212 -
5.213 - def __repr__(self):
5.214 - return "Attribute(%s, %s)" % (repr(self.context), repr(self.type))
5.215 -
5.216 - def __hash__(self):
5.217 - return id(self.type)
5.218 -
5.219 -# Configuration setting.
5.220 -
5.221 -Class = SingleInstanceClass
5.222 -#Class = MultipleInstanceClass
5.223 -
5.224 -def set_single_instance_mode():
5.225 - global Class
5.226 - Class = SingleInstanceClass
5.227 -
5.228 -def set_multiple_instance_mode():
5.229 - global Class
5.230 - Class = MultipleInstanceClass
5.231 -
5.232 -def set_selective_multiple_instance_mode():
5.233 - global Class
5.234 - Class = SelectiveMultipleInstanceClass
5.235 -
5.236 -def set_prolific_multiple_instance_mode():
5.237 - global Class
5.238 - Class = ProlificMultipleInstanceClass
5.239 -
5.240 -# vim: tabstop=4 expandtab shiftwidth=4
6.1 --- a/simplified/program.py Sun May 27 18:19:01 2007 +0200
6.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
6.3 @@ -1,412 +0,0 @@
6.4 -#!/usr/bin/env python
6.5 -
6.6 -"""
6.7 -Simplified program nodes for easier type propagation and analysis. This module
6.8 -contains nodes representing program instructions or operations, program
6.9 -structure or organisation, and abstract program data.
6.10 -
6.11 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
6.12 -
6.13 -This software is free software; you can redistribute it and/or
6.14 -modify it under the terms of the GNU General Public License as
6.15 -published by the Free Software Foundation; either version 2 of
6.16 -the License, or (at your option) any later version.
6.17 -
6.18 -This software is distributed in the hope that it will be useful,
6.19 -but WITHOUT ANY WARRANTY; without even the implied warranty of
6.20 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.21 -GNU General Public License for more details.
6.22 -
6.23 -You should have received a copy of the GNU General Public
6.24 -License along with this library; see the file LICENCE.txt
6.25 -If not, write to the Free Software Foundation, Inc.,
6.26 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
6.27 -"""
6.28 -
6.29 -from simplified.utils import Structure, WithName, name
6.30 -import sys
6.31 -
6.32 -# Simplified program nodes.
6.33 -
6.34 -class Node:
6.35 -
6.36 - """
6.37 - A result node with common attributes:
6.38 -
6.39 - original The original node from which this node was created.
6.40 - defining Whether the node defines something in the original program.
6.41 - name Any name involved (variable or attribute).
6.42 - index Any index involved (temporary variable name).
6.43 - value Any constant value.
6.44 - ref Any reference to (for example) subprograms.
6.45 - nstype Any indication of the namespace type involved in a name access.
6.46 -
6.47 - Expression-related attributes:
6.48 -
6.49 - expr Any contributing expression.
6.50 - lvalue Any target expression.
6.51 - test Any test expression in a conditional instruction.
6.52 -
6.53 - Invocation and subprogram attributes:
6.54 -
6.55 - args Any collection of argument nodes.
6.56 - params Any collection of parameter nodes and defaults.
6.57 -
6.58 - Tuple construction attributes:
6.59 -
6.60 - nodes Any expressions used to initialise a tuple
6.61 -
6.62 - Statement-grouping attributes:
6.63 -
6.64 - body Any conditional code depending on the success of a test.
6.65 - else_ Any conditional code depending on the failure of a test.
6.66 - handler Any exception handler code.
6.67 - finally_ Any code which will be executed regardless.
6.68 - code Any unconditional code.
6.69 - choices Any choices which may be included in the final program.
6.70 - """
6.71 -
6.72 - common_attributes = "name", "index", "value", "nstype", "internal", "returns_value", "is_method", "ref", "module", "structures", "original"
6.73 - expression_attributes = "expr", "lvalue", "test"
6.74 - argument_attributes = "star", "dstar"
6.75 - invocation_attributes = "params", # not "args" - see "pos_args", "kw_args"
6.76 - grouping_attributes = "code", "body", "else_", "handler", "finally_", "choices"
6.77 -
6.78 - def __init__(self, original=None, defining=0, **kw):
6.79 -
6.80 - """
6.81 - Initialise a program node with a link to an optional 'original' AST
6.82 - node. An optional 'defining' parameter (if set to a true value), sets
6.83 - this node as the defining node in the original.
6.84 - """
6.85 -
6.86 - self.original = original
6.87 - self.defining = defining
6.88 - self.copies = {}
6.89 -
6.90 - if self.original is not None and defining:
6.91 - self.original._node = self
6.92 - for name, value in kw.items():
6.93 - setattr(self, name, value)
6.94 -
6.95 - # Annotations.
6.96 -
6.97 - self.types = set()
6.98 -
6.99 - def __repr__(self):
6.100 -
6.101 - "Return a readable representation."
6.102 -
6.103 - if hasattr(self, "full_name"):
6.104 - s = "%s '%s'" % (self.__class__.__name__, self.full_name())
6.105 - elif hasattr(self, "name"):
6.106 - s = "%s '%s'" % (self.__class__.__name__, self.name)
6.107 - elif hasattr(self, "index"):
6.108 - s = "%s (%s)" % (self.__class__.__name__, self.index)
6.109 - elif hasattr(self, "value"):
6.110 - s = "%s %s" % (self.__class__.__name__, repr(self.value))
6.111 - elif hasattr(self, "ref"):
6.112 - s = "%s '%s'" % (self.__class__.__name__, name(self.ref, self.ref.name))
6.113 - else:
6.114 - s = "%s" % (self.__class__.__name__,)
6.115 -
6.116 - # Annotations.
6.117 -
6.118 - if self.types:
6.119 - return "%s -> %s" % (s, self.types)
6.120 - else:
6.121 - return s
6.122 -
6.123 - def _pprint(self, indent, continuation, s, stream=None):
6.124 -
6.125 - """
6.126 - Print, at the given 'indent' level, with the given 'continuation' text,
6.127 - the string 's', either to the given, optional 'stream' or to standard
6.128 - output, this node's "pretty" representation.
6.129 - """
6.130 -
6.131 - stream = stream or sys.stdout
6.132 - if continuation:
6.133 - print >>stream, (" " * max(0, indent - len(continuation))) + continuation + s
6.134 - else:
6.135 - print >>stream, (" " * indent) + s
6.136 -
6.137 - def pprint(self, indent=0, continuation=None, stream=None):
6.138 -
6.139 - """
6.140 - Print, at the given, optional 'indent', with the given optional
6.141 - 'continuation' text, either to the given, optional 'stream' or to
6.142 - standard output, this node's "pretty" representation along with its
6.143 - children and their "pretty" representation (and so on).
6.144 - """
6.145 -
6.146 - stream = stream or sys.stdout
6.147 - self._pprint(indent, continuation, repr(self), stream)
6.148 -
6.149 - # Subprogram-related details.
6.150 -
6.151 - if hasattr(self, "params"):
6.152 - for name, default in self.params:
6.153 - self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
6.154 - if hasattr(self, "star") and self.star:
6.155 - name, default = self.star
6.156 - self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
6.157 - if hasattr(self, "dstar") and self.dstar:
6.158 - name, default = self.dstar
6.159 - self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
6.160 - if getattr(self, "internal", 0):
6.161 - self._pprint(indent + 2, "( ", "internal", stream=stream)
6.162 - if getattr(self, "structure", 0):
6.163 - self._pprint(indent + 2, "( ", "structure '%s'" % self.structure.name, stream=stream)
6.164 -
6.165 - # Expression-related details.
6.166 -
6.167 - if hasattr(self, "expr"):
6.168 - self.expr.pprint(indent + 2, "- ", stream=stream)
6.169 - if hasattr(self, "nodes"):
6.170 - for node in self.nodes:
6.171 - node.pprint(indent + 2, "- ", stream=stream)
6.172 - if hasattr(self, "lvalue"):
6.173 - self.lvalue.pprint(indent + 2, "->", stream=stream)
6.174 - if hasattr(self, "nstype"):
6.175 - self._pprint(indent + 2, "", self.nstype, stream=stream)
6.176 - if hasattr(self, "args"):
6.177 - for arg in self.pos_args:
6.178 - arg.pprint(indent + 2, "( ", stream=stream)
6.179 - for name, arg in self.kw_args.items():
6.180 - arg.pprint(indent + 2, "( ", stream=stream)
6.181 - if hasattr(self, "star") and self.star:
6.182 - self.star.pprint(indent + 2, "( ", stream=stream)
6.183 - if hasattr(self, "dstar") and self.dstar:
6.184 - self.dstar.pprint(indent + 2, "( ", stream=stream)
6.185 -
6.186 - # Statement-related details.
6.187 -
6.188 - if hasattr(self, "test"):
6.189 - self.test.pprint(indent + 2, "? ", stream=stream)
6.190 - for attr in self.grouping_attributes:
6.191 - if hasattr(self, attr) and getattr(self, attr):
6.192 - self._pprint(indent, "", "%s {" % attr, stream=stream)
6.193 - for node in getattr(self, attr):
6.194 - node.pprint(indent + 2, stream=stream)
6.195 - self._pprint(indent, "", "}", stream=stream)
6.196 -
6.197 - # Annotations.
6.198 -
6.199 - if hasattr(self, "accesses"):
6.200 - self._pprint(indent, "", "--------", stream=stream)
6.201 - for ref, attributes in self.accesses.items():
6.202 - self._pprint(indent + 2, "| ", "when %s: %s" % (ref, ", ".join([("%s via %s" % attr_acc) for attr_acc in attributes])), stream=stream)
6.203 - self._pprint(indent, "", "--------", stream=stream)
6.204 - if hasattr(self, "writes"):
6.205 - self._pprint(indent, "", "--------", stream=stream)
6.206 - for ref, attribute in self.writes.items():
6.207 - self._pprint(indent + 2, "| ", "when %s: %s" % (ref, attribute), stream=stream)
6.208 - self._pprint(indent, "", "--------", stream=stream)
6.209 -
6.210 - # Node discovery functions.
6.211 -
6.212 - def active(self):
6.213 -
6.214 - "Return the active copies of this node or a list containing this node."
6.215 -
6.216 - return self.copies.values() or [self]
6.217 -
6.218 - # Node manipulation functions.
6.219 -
6.220 - def copy(self, instance=None, new_name=None):
6.221 -
6.222 - """
6.223 - Perform a deep copy of the node, optionally specifying the 'instance'
6.224 - for whom the copy has been requested and a 'new_name' for the copied
6.225 - node. Return new unannotated copies of the node and its descendants.
6.226 - """
6.227 -
6.228 - # Copy the common attributes of this node.
6.229 -
6.230 - common = {}
6.231 - for attr in self.common_attributes:
6.232 - if hasattr(self, attr):
6.233 - common[attr] = getattr(self, attr)
6.234 -
6.235 - # Add new attributes specially for copies.
6.236 -
6.237 - common["instance"] = instance
6.238 -
6.239 - if new_name is not None:
6.240 - common["copy_of"] = self
6.241 - common["name"] = new_name
6.242 -
6.243 - # Instantiate the copy, avoiding side-effects with original and defining.
6.244 -
6.245 - node = self.__class__(**common)
6.246 - node.defining = self.defining
6.247 -
6.248 - # Add links to copies from originals.
6.249 -
6.250 - self.copies[instance] = node
6.251 -
6.252 - # Copy attributes of different types.
6.253 -
6.254 - for attr in self.expression_attributes:
6.255 - if hasattr(self, attr):
6.256 - n = getattr(self, attr)
6.257 - if n is None:
6.258 - n2 = n
6.259 - else:
6.260 - n2 = n.copy(instance)
6.261 - setattr(node, attr, n2)
6.262 -
6.263 - for attr in self.argument_attributes:
6.264 - if hasattr(self, attr):
6.265 - t = getattr(self, attr)
6.266 - if t is None:
6.267 - t2 = t
6.268 - else:
6.269 - name, n = t
6.270 - n2 = n.copy(instance)
6.271 - t2 = name, n2
6.272 - setattr(node, attr, t2)
6.273 -
6.274 - for attr in self.invocation_attributes:
6.275 - if hasattr(self, attr):
6.276 - l = getattr(self, attr)
6.277 - l2 = []
6.278 - for name, n in l:
6.279 - if n is None:
6.280 - l2.append((name, n))
6.281 - else:
6.282 - l2.append((name, n.copy(instance)))
6.283 - setattr(node, attr, l2)
6.284 -
6.285 - for attr in self.grouping_attributes:
6.286 - if hasattr(self, attr):
6.287 - l = getattr(self, attr)
6.288 - setattr(node, attr, [n.copy(instance) for n in l])
6.289 -
6.290 - # Arguments are usually processed further - "args" is useless.
6.291 -
6.292 - if hasattr(self, "pos_args"):
6.293 - node.pos_args = [n.copy(instance) for n in self.pos_args]
6.294 -
6.295 - if hasattr(self, "kw_args"):
6.296 - node.kw_args = {}
6.297 - for name, n in self.kw_args.items():
6.298 - node.kw_args[name] = n.copy(instance)
6.299 -
6.300 - return node
6.301 -
6.302 -# These are the supported "operations" described by simplified program nodes.
6.303 -
6.304 -class Pass(Node): "A placeholder node corresponding to pass."
6.305 -class Assign(Node): "A grouping node for assignment-related operations."
6.306 -class Keyword(Node): "A grouping node for keyword arguments."
6.307 -class Global(Node): "A global name designator."
6.308 -class Import(Node): "A module import operation."
6.309 -class LoadTemp(Node): "Load a previously-stored temporary value."
6.310 -class LoadName(Node): "Load a named object."
6.311 -class LoadAttr(Node): "Load an object attribute."
6.312 -class LoadRef(Node): "Load a reference, typically a subprogram or a constant."
6.313 -class LoadExc(Node): "Load a handled exception."
6.314 -class ResetExc(Node): "Reset the exception state."
6.315 -class StoreTemp(Node): "Store a temporary value."
6.316 -class StoreName(Node): "Associate a name with an object."
6.317 -class StoreAttr(Node): "Associate an object's attribute with a value."
6.318 -class ReleaseTemp(Node): "Release a temporary value."
6.319 -class Try(Node): "A try...except...else...finally grouping node."
6.320 -class Raise(Node): "An exception raising node."
6.321 -class Not(Node): "A negation of an expression."
6.322 -class CheckType(Node): "Check a value's type from a list of choices."
6.323 -class Return(Node): "Return an evaluated expression."
6.324 -class Invoke(Node): "An invocation."
6.325 -class MakeTuple(Node): "Make a tuple object."
6.326 -
6.327 -# There are two types of return node: return from function and return from
6.328 -# block.
6.329 -
6.330 -class ReturnFromFunction(Return):
6.331 - pass
6.332 -
6.333 -class ReturnFromBlock(Return):
6.334 - pass
6.335 -
6.336 -# NOTE: Not actually supported.
6.337 -# Additionally, yield statements act like return statements for the purposes
6.338 -# of this system.
6.339 -
6.340 -class Yield(ReturnFromFunction):
6.341 - pass
6.342 -
6.343 -# Some behaviour is set as the default in conditional nodes but may be
6.344 -# overridden.
6.345 -
6.346 -class Conditional(Node):
6.347 -
6.348 - "A conditional node consisting of a test and outcomes."
6.349 -
6.350 - def __init__(self, *args, **kw):
6.351 - self.isolate_test = 0
6.352 - Node.__init__(self, *args, **kw)
6.353 -
6.354 -# Invocations involve some more work to process calculated attributes.
6.355 -
6.356 -class InvokeFunction(Invoke):
6.357 -
6.358 - "A function or method invocation."
6.359 -
6.360 - def __init__(self, *args, **kw):
6.361 - self.args = []
6.362 - self.star = None
6.363 - self.dstar = None
6.364 - Invoke.__init__(self, *args, **kw)
6.365 - self.set_args(self.args)
6.366 - self.share_locals = 0
6.367 -
6.368 - def set_args(self, args):
6.369 -
6.370 - "Sort the 'args' into positional and keyword arguments."
6.371 -
6.372 - self.pos_args = []
6.373 - self.kw_args = {}
6.374 - add_kw = 0
6.375 - for arg in args:
6.376 - if not add_kw:
6.377 - if not isinstance(arg, Keyword):
6.378 - self.pos_args.append(arg)
6.379 - else:
6.380 - add_kw = 1
6.381 - if add_kw:
6.382 - if isinstance(arg, Keyword):
6.383 - self.kw_args[arg.name] = arg.expr
6.384 - else:
6.385 - raise TypeError, "Positional argument appears after keyword arguments in '%s'." % self
6.386 -
6.387 -class InvokeRef(Invoke):
6.388 -
6.389 - "A block or loop invocation."
6.390 -
6.391 - def __init__(self, *args, **kw):
6.392 - self.share_locals = 1
6.393 - Invoke.__init__(self, *args, **kw)
6.394 -
6.395 -# Program structure nodes.
6.396 -
6.397 -class Module(Node, Structure):
6.398 -
6.399 - "A Python module."
6.400 -
6.401 - def full_name(self):
6.402 - return "module %s" % self.name
6.403 -
6.404 -class Subprogram(Node, WithName):
6.405 -
6.406 - "A subprogram: functions, methods and loops."
6.407 -
6.408 - def __init__(self, *args, **kw):
6.409 - Node.__init__(self, *args, **kw)
6.410 - WithName.__init__(self)
6.411 - self.raises = set()
6.412 - self.returns = set()
6.413 - self.return_locals = set()
6.414 -
6.415 -# vim: tabstop=4 expandtab shiftwidth=4
7.1 --- a/simplified/utils.py Sun May 27 18:19:01 2007 +0200
7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
7.3 @@ -1,167 +0,0 @@
7.4 -#!/usr/bin/env python
7.5 -
7.6 -"""
7.7 -Simplified program utilities.
7.8 -
7.9 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
7.10 -
7.11 -This software is free software; you can redistribute it and/or
7.12 -modify it under the terms of the GNU General Public License as
7.13 -published by the Free Software Foundation; either version 2 of
7.14 -the License, or (at your option) any later version.
7.15 -
7.16 -This software is distributed in the hope that it will be useful,
7.17 -but WITHOUT ANY WARRANTY; without even the implied warranty of
7.18 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7.19 -GNU General Public License for more details.
7.20 -
7.21 -You should have received a copy of the GNU General Public
7.22 -License along with this library; see the file LICENCE.txt
7.23 -If not, write to the Free Software Foundation, Inc.,
7.24 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
7.25 -"""
7.26 -
7.27 -from compiler.visitor import ASTVisitor
7.28 -
7.29 -# Exceptions.
7.30 -
7.31 -class SimplifiedError(Exception):
7.32 -
7.33 - "An error in the annotation process."
7.34 -
7.35 - def __init__(self, exc, node, *args):
7.36 -
7.37 - """
7.38 - Initialise the error with an existing exception 'exc', the 'node' at
7.39 - which this error occurs, along with additional optional arguments.
7.40 - """
7.41 -
7.42 - Exception.__init__(self, *args)
7.43 - self.nodes = [node]
7.44 - self.exc = exc
7.45 -
7.46 - def add(self, node):
7.47 -
7.48 - "Add the given 'node' to the path of nodes leading from the exception."
7.49 -
7.50 - self.nodes.append(node)
7.51 -
7.52 - def __str__(self):
7.53 -
7.54 - "Return a string showing the principal exception details."
7.55 -
7.56 - return "%s, %s" % (self.exc, self.nodes)
7.57 -
7.58 -# Elementary visitor support.
7.59 -
7.60 -class Visitor(ASTVisitor):
7.61 -
7.62 - "A visitor base class."
7.63 -
7.64 - def __init__(self):
7.65 - ASTVisitor.__init__(self)
7.66 -
7.67 - def default(self, node, *args):
7.68 - raise SimplifiedError, (None, node)
7.69 -
7.70 - def dispatch(self, node, *args):
7.71 - return ASTVisitor.dispatch(self, node, *args)
7.72 -
7.73 - def dispatches(self, nodes, *args):
7.74 - results = []
7.75 - for node in nodes:
7.76 - results.append(self.dispatch(node, *args))
7.77 - return results
7.78 -
7.79 - def dispatch_dict(self, d, *args):
7.80 - results = {}
7.81 - for name, node in d.items():
7.82 - results[name] = self.dispatch(node, *args)
7.83 - return results
7.84 -
7.85 -# Unique name registration.
7.86 -
7.87 -class Naming:
7.88 -
7.89 - "Maintain records of unique names for each simple name."
7.90 -
7.91 - index_separator = "-"
7.92 -
7.93 - def __init__(self):
7.94 - self.names = {}
7.95 -
7.96 - def get(self, obj):
7.97 - return obj._unique_name
7.98 -
7.99 - def set(self, obj, name):
7.100 - if hasattr(obj, "_unique_name"):
7.101 - return
7.102 - if not self.names.has_key(name):
7.103 - self.names[name] = 0
7.104 - n = self.names[name] + 1
7.105 - self.names[name] = n
7.106 - obj._unique_name = "%s%s%d" % (name, self.index_separator, n)
7.107 -
7.108 -def name(obj, name):
7.109 -
7.110 - "Return a unique name for the given 'obj', indicating the base 'name'."
7.111 -
7.112 - naming.set(obj, name)
7.113 - return naming.get(obj)
7.114 -
7.115 -# Naming singleton.
7.116 -
7.117 -naming = Naming()
7.118 -
7.119 -# Named nodes are those which can be referenced in some way.
7.120 -
7.121 -class WithName:
7.122 -
7.123 - "Node naming."
7.124 -
7.125 - def __init__(self):
7.126 -
7.127 - "Initialise the object's full name."
7.128 -
7.129 - self._full_name = name(self, self.name or "$untitled")
7.130 -
7.131 - def full_name(self):
7.132 -
7.133 - "Return the object's full name."
7.134 -
7.135 - return self._full_name
7.136 -
7.137 -# Comparable nodes based on naming.
7.138 -
7.139 -class Comparable:
7.140 -
7.141 - "Comparable nodes implementing the 'full_name' method."
7.142 -
7.143 - def __eq__(self, other):
7.144 -
7.145 - "This object is equal to 'other' if the full names are the same."
7.146 -
7.147 - # NOTE: Single instance: all instances are the same
7.148 - # NOTE: Multiple instances: all instances are different
7.149 - if hasattr(other, "full_name"):
7.150 - return self.full_name() == other.full_name()
7.151 - else:
7.152 - return NotImplemented
7.153 -
7.154 - def __hash__(self):
7.155 -
7.156 - "The hash of this object is based on its full name."
7.157 -
7.158 - return hash(self.full_name())
7.159 -
7.160 -# Structure nodes indicating namespace-bearing objects.
7.161 -
7.162 -class Structure(Comparable):
7.163 -
7.164 - "A non-program node containing some kind of namespace."
7.165 -
7.166 - def __init__(self, **kw):
7.167 - for name, value in kw.items():
7.168 - setattr(self, name, value)
7.169 -
7.170 -# vim: tabstop=4 expandtab shiftwidth=4
8.1 --- a/simplify.py Sun May 27 18:19:01 2007 +0200
8.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
8.3 @@ -1,1941 +0,0 @@
8.4 -#!/usr/bin/env python
8.5 -
8.6 -"""
8.7 -Simplify AST structures for easier type propagation and analysis. The code in
8.8 -this module processes AST trees originating from the compiler module and
8.9 -produces a result tree consisting of instruction-oriented program nodes.
8.10 -
8.11 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
8.12 -
8.13 -This software is free software; you can redistribute it and/or
8.14 -modify it under the terms of the GNU General Public License as
8.15 -published by the Free Software Foundation; either version 2 of
8.16 -the License, or (at your option) any later version.
8.17 -
8.18 -This software is distributed in the hope that it will be useful,
8.19 -but WITHOUT ANY WARRANTY; without even the implied warranty of
8.20 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8.21 -GNU General Public License for more details.
8.22 -
8.23 -You should have received a copy of the GNU General Public
8.24 -License along with this library; see the file LICENCE.txt
8.25 -If not, write to the Free Software Foundation, Inc.,
8.26 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
8.27 -
8.28 ---------
8.29 -
8.30 -To use this module, the easiest approach is to use the simplify function:
8.31 -
8.32 -simplify(filename)
8.33 -
8.34 -The more complicated approach involves first instantiating a Simplifier object:
8.35 -
8.36 -simplifier = Simplifier()
8.37 -
8.38 -Then, applying the simplifier to an AST tree:
8.39 -
8.40 -module = compiler.parseFile(filename)
8.41 -simplifier.process(module)
8.42 -"""
8.43 -
8.44 -from simplified import *
8.45 -import compiler.ast
8.46 -import os
8.47 -
8.48 -class Simplifier(Visitor):
8.49 -
8.50 - """
8.51 - A simplifying visitor for AST nodes.
8.52 -
8.53 - Covered: Add, And, Assert, AssAttr, AssList, AssName, AssTuple, Assign,
8.54 - AugAssign, Bitand, Break, CallFunc, Class, Compare, Const,
8.55 - Continue, Dict, Discard, Div, FloorDiv, For, From, Function,
8.56 - Getattr, Global, If, Import, Invert, Keyword, Lambda, List,
8.57 - ListComp, ListCompFor, ListCompIf, Mod, Module, Mul, Name, Not, Or,
8.58 - Pass, Power, Print, Printnl, Raise, Return, Slice, Sliceobj, Stmt,
8.59 - Sub, Subscript, TryExcept, TryFinally, Tuple, While, UnaryAdd,
8.60 - UnarySub.
8.61 -
8.62 - Missing: Backquote, Bitor, Bitxor, Decorators, Ellipsis, Exec, LeftShift,
8.63 - RightShift, Yield.
8.64 - """
8.65 -
8.66 - def __init__(self, builtins=0):
8.67 -
8.68 - """
8.69 - Initialise the simplifier with the optional 'builtins' parameter
8.70 - indicating whether the module contains the built-in classes and
8.71 - functions.
8.72 - """
8.73 -
8.74 - Visitor.__init__(self)
8.75 - self.subprograms = [] # Subprograms outside the tree.
8.76 - self.structures = [] # Structures/classes.
8.77 - self.constants = {} # Constants.
8.78 - self.current_subprograms = [] # Current subprograms being processed.
8.79 - self.current_structures = [] # Current structures being processed.
8.80 - self.within_class = 0 # Whether a class is being defined.
8.81 - self.builtins = builtins # Whether the builtins are being processed.
8.82 -
8.83 - # Convenience attributes.
8.84 -
8.85 - self.subnames = {}
8.86 -
8.87 - # For compiler package mechanisms.
8.88 -
8.89 - self.visitor = self
8.90 -
8.91 - def process(self, node, name):
8.92 - result = self.dispatch(node, name)
8.93 - result.simplifier = self
8.94 - return result
8.95 -
8.96 - def dispatch_or_none(self, node, *args):
8.97 - if node is not None:
8.98 - return self.dispatch(node, *args)
8.99 - else:
8.100 - return LoadName(node, name="None")
8.101 -
8.102 - # Top-level transformation.
8.103 -
8.104 - def visitModule(self, module, name=None):
8.105 -
8.106 - """
8.107 - Process the given 'module', producing a Module object which contains the
8.108 - resulting program nodes. If the optional 'name' is provided, the 'name'
8.109 - attribute is set on the Module object using a value other than None.
8.110 - """
8.111 -
8.112 - result = self.module = Module(module, 1, name=name)
8.113 - result.code = self.dispatch(module.node)
8.114 - return result
8.115 -
8.116 - # Node transformations.
8.117 -
8.118 - def visitAdd(self, add):
8.119 - return self._visitBinary(add, "__add__", "__radd__")
8.120 -
8.121 - def visitAnd(self, and_):
8.122 -
8.123 - """
8.124 - Make a subprogram for the 'and_' node and record its contents inside the
8.125 - subprogram. Convert...
8.126 -
8.127 - And (test)
8.128 - (test)
8.129 - ...
8.130 -
8.131 - ...to:
8.132 -
8.133 - Subprogram -> Conditional (test) -> ReturnFromBlock ...
8.134 - (else) -> Conditional (test) -> ReturnFromBlock ...
8.135 - (else) -> ...
8.136 - """
8.137 -
8.138 - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
8.139 - self.current_subprograms.append(subprogram)
8.140 -
8.141 - # In the subprogram, make instructions which store each operand, test
8.142 - # for each operand's truth status, and if appropriate return from the
8.143 - # subprogram with the value of the operand.
8.144 -
8.145 - last = and_.nodes[-1]
8.146 - results = nodes = []
8.147 -
8.148 - for node in and_.nodes:
8.149 - expr = self.dispatch(node)
8.150 -
8.151 - # Return from the subprogram where the test is not satisfied.
8.152 -
8.153 - if node is not last:
8.154 - nodes += [
8.155 - StoreTemp(expr=expr),
8.156 - Conditional(
8.157 - test=self._visitNot(LoadTemp()),
8.158 - body=[
8.159 - ReturnFromBlock(
8.160 - expr=LoadTemp()
8.161 - )
8.162 - ],
8.163 - else_=[
8.164 - ReleaseTemp()
8.165 - # Subsequent operations go here!
8.166 - ]
8.167 - )
8.168 - ]
8.169 -
8.170 - # Put subsequent operations in the else section of this conditional.
8.171 -
8.172 - nodes = nodes[-1].else_
8.173 -
8.174 - # For the last operation, return the result.
8.175 -
8.176 - else:
8.177 - nodes.append(ReturnFromBlock(expr=expr))
8.178 -
8.179 - # Finish the subprogram definition.
8.180 -
8.181 - subprogram.code = results
8.182 -
8.183 - self.current_subprograms.pop()
8.184 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.185 -
8.186 - # Make an invocation of the subprogram.
8.187 -
8.188 - result = InvokeRef(and_, 1, produces_result=1, ref=subprogram)
8.189 - return result
8.190 -
8.191 - def visitAssert(self, assert_):
8.192 - if assert_.fail:
8.193 - fail_args = [self.dispatch(assert_.fail)]
8.194 - else:
8.195 - fail_args = []
8.196 -
8.197 - result = Conditional(assert_, 1,
8.198 - test=self.dispatch(assert_.test),
8.199 - body=[],
8.200 - else_=[
8.201 - Raise(assert_,
8.202 - expr=InvokeFunction(assert_,
8.203 - expr=LoadName(name="AssertionError"),
8.204 - args=fail_args,
8.205 - star=None,
8.206 - dstar=None
8.207 - )
8.208 - )
8.209 - ]
8.210 - )
8.211 -
8.212 - # Make nice annotations for the viewer.
8.213 -
8.214 - assert_._raises = result.else_[0].expr
8.215 - return result
8.216 -
8.217 - # Assignments.
8.218 -
8.219 - def visitAssAttr(self, assattr, in_sequence=0):
8.220 - expr = self._visitAssNameOrAttr(assattr, in_sequence)
8.221 - lvalue = self.dispatch(assattr.expr)
8.222 - result = StoreAttr(assattr, 1, name=assattr.attrname, lvalue=lvalue, expr=expr)
8.223 - return result
8.224 -
8.225 - def visitAssign(self, assign):
8.226 - result = Assign(assign, 1)
8.227 - store = StoreTemp(expr=self.dispatch(assign.expr))
8.228 - release = ReleaseTemp()
8.229 - result.code = [store] + self.dispatches(assign.nodes, 0) + [release]
8.230 - return result
8.231 -
8.232 - def visitAssList(self, asslist, in_sequence=0):
8.233 - if not in_sequence:
8.234 - expr = LoadTemp()
8.235 - else:
8.236 - expr = InvokeFunction(asslist, expr=LoadAttr(expr=LoadTemp(), name="next"))
8.237 - result = Assign(asslist, 1)
8.238 - store = StoreTemp(expr=InvokeFunction(asslist, expr=LoadAttr(name="__iter__", expr=expr)))
8.239 - release = ReleaseTemp()
8.240 - result.code = [store] + self.dispatches(asslist.nodes, 1) + [release]
8.241 - return result
8.242 -
8.243 - visitAssTuple = visitAssList
8.244 -
8.245 - def _visitAssNameOrAttr(self, node, in_sequence):
8.246 - if not in_sequence:
8.247 - return LoadTemp()
8.248 - else:
8.249 - return InvokeFunction(node, expr=LoadAttr(expr=LoadTemp(), name="next"))
8.250 -
8.251 - def visitAssName(self, assname, in_sequence=0):
8.252 - expr = self._visitAssNameOrAttr(assname, in_sequence)
8.253 - result = StoreName(assname, 1, name=assname.name, expr=expr)
8.254 - return result
8.255 -
8.256 - augassign_methods = {
8.257 - "+=" : "__iadd__", "-=" : "__isub__", "*=" : "__imul__", "/=" : "__idiv__",
8.258 - "%=" : "__imod__", "**=" : "__ipow__", "<<=" : "__ilshift__", ">>=" : "__irshift__",
8.259 - "&=" : "__iand__", "^=" : "__ixor__", "|=" : "__ior__"
8.260 - }
8.261 -
8.262 - def visitAugAssign(self, augassign):
8.263 -
8.264 - """
8.265 - Convert the augmented assignment...
8.266 -
8.267 - AugAssign (node) -> Name | Getattr | Slice | Subscript
8.268 - (op)
8.269 - (expr)
8.270 -
8.271 - ...to:
8.272 -
8.273 - Assign (code) -> StoreTemp (expr) -> InvokeFunction (expr) -> LoadAttr (expr) -> <name>
8.274 - (name) -> <op>
8.275 - StoreName (name) -> <name>
8.276 - (expr) -> LoadTemp
8.277 - ReleaseTemp
8.278 - """
8.279 -
8.280 - result = Assign(augassign, 1)
8.281 - expr = self.dispatch(augassign.expr)
8.282 -
8.283 - # Simple augmented assignment: name += expr
8.284 -
8.285 - if isinstance(augassign.node, compiler.ast.Name):
8.286 - result.code = [
8.287 - StoreTemp(
8.288 - expr=InvokeFunction( # referenced below
8.289 - augassign,
8.290 - args=[expr],
8.291 - star=None,
8.292 - dstar=None,
8.293 - expr=LoadAttr(
8.294 - expr=self.dispatch(augassign.node),
8.295 - name=self.augassign_methods[augassign.op]
8.296 - )
8.297 - )
8.298 - ),
8.299 - StoreName(
8.300 - expr=LoadTemp(),
8.301 - name=augassign.node.name),
8.302 - ReleaseTemp()
8.303 - ]
8.304 -
8.305 - # Make nice annotations for the viewer.
8.306 -
8.307 - augassign._op_call = result.code[0].expr
8.308 -
8.309 - # Complicated augmented assignment: lvalue.attr += expr
8.310 -
8.311 - elif isinstance(augassign.node, compiler.ast.Getattr):
8.312 -
8.313 - # <lvalue> -> setattr(<lvalue>, getattr(<lvalue>, "attr").__xxx__(expr))
8.314 -
8.315 - result.code = [
8.316 - StoreTemp(
8.317 - index="expr",
8.318 - expr=self.dispatch(augassign.node.expr)
8.319 - ),
8.320 - StoreTemp(
8.321 - expr=InvokeFunction( # referenced below
8.322 - augassign,
8.323 - args=[expr], star=None, dstar=None,
8.324 - expr=LoadAttr(
8.325 - expr=LoadAttr(augassign.node, 1,
8.326 - expr=LoadTemp(index="expr"),
8.327 - name=augassign.node.attrname
8.328 - ),
8.329 - name=self.augassign_methods[augassign.op]
8.330 - )
8.331 - )
8.332 - ),
8.333 - StoreAttr(
8.334 - expr=LoadTemp(),
8.335 - lvalue=LoadTemp(index="expr"),
8.336 - name=augassign.node.attrname
8.337 - ),
8.338 - ReleaseTemp(index="expr"),
8.339 - ReleaseTemp()
8.340 - ]
8.341 -
8.342 - # Make nice annotations for the viewer.
8.343 -
8.344 - augassign._op_call = result.code[1].expr
8.345 -
8.346 - # Complicated augassign using slices: lvalue[lower:upper] += expr
8.347 -
8.348 - elif isinstance(augassign.node, compiler.ast.Slice):
8.349 -
8.350 - # <lvalue>, <lower>, <upper> -> <lvalue>.__setslice__(<lower>, <upper>, <lvalue>.__getslice__(<lower>, <upper>).__xxx__(expr))
8.351 -
8.352 - result.code = [
8.353 - StoreTemp(
8.354 - index="expr",
8.355 - expr=self.dispatch(augassign.node.expr)
8.356 - ),
8.357 - StoreTemp(
8.358 - index="lower",
8.359 - expr=self.dispatch_or_none(augassign.node.lower)
8.360 - ),
8.361 - StoreTemp(
8.362 - index="upper",
8.363 - expr=self.dispatch_or_none(augassign.node.upper)
8.364 - ),
8.365 - StoreTemp(
8.366 - expr=InvokeFunction( # referenced below
8.367 - augassign,
8.368 - args=[expr], star=None, dstar=None,
8.369 - expr=LoadAttr(
8.370 - expr=self._visitSlice(
8.371 - augassign.node,
8.372 - LoadTemp(index="expr"),
8.373 - LoadTemp(index="lower"),
8.374 - LoadTemp(index="upper"),
8.375 - "OP_APPLY"),
8.376 - name=self.augassign_methods[augassign.op]
8.377 - )
8.378 - )
8.379 - ),
8.380 - self._visitSlice(
8.381 - augassign.node,
8.382 - LoadTemp(index="expr"),
8.383 - LoadTemp(index="lower"),
8.384 - LoadTemp(index="upper"),
8.385 - "OP_ASSIGN",
8.386 - LoadTemp()
8.387 - ),
8.388 - ReleaseTemp(index="expr"),
8.389 - ReleaseTemp(index="lower"),
8.390 - ReleaseTemp(index="upper"),
8.391 - ReleaseTemp()
8.392 - ]
8.393 -
8.394 - # Make nice annotations for the viewer.
8.395 -
8.396 - augassign._op_call = result.code[3].expr
8.397 -
8.398 - # Complicated augassign using subscripts: lvalue[subs] += expr
8.399 -
8.400 - elif isinstance(augassign.node, compiler.ast.Subscript):
8.401 -
8.402 - # <lvalue>, <subs> -> <lvalue>.__setitem__(<subs>, <lvalue>.__getitem__(<subs>).__xxx__(expr))
8.403 -
8.404 - result.code = [
8.405 - StoreTemp(index="expr", expr=self.dispatch(augassign.node.expr)),
8.406 - StoreTemp(index="subs", expr=self._visitSubscriptSubs(augassign.node, augassign.node.subs)),
8.407 - StoreTemp(
8.408 - expr=InvokeFunction( # referenced below
8.409 - augassign,
8.410 - args=[expr], star=None, dstar=None,
8.411 - expr=LoadAttr(
8.412 - expr=self._visitSubscript(
8.413 - augassign.node,
8.414 - LoadTemp(index="expr"),
8.415 - LoadTemp(index="subs"),
8.416 - "OP_APPLY"
8.417 - ),
8.418 - name=self.augassign_methods[augassign.op]
8.419 - )
8.420 - )
8.421 - ),
8.422 - self._visitSubscript(
8.423 - augassign.node,
8.424 - LoadTemp(index="expr"),
8.425 - LoadTemp(index="subs"),
8.426 - "OP_ASSIGN",
8.427 - LoadTemp()
8.428 - ),
8.429 - ReleaseTemp(index="expr"),
8.430 - ReleaseTemp(index="subs"),
8.431 - ReleaseTemp()
8.432 - ]
8.433 -
8.434 - # Make nice annotations for the viewer.
8.435 -
8.436 - augassign._op_call = result.code[2].expr
8.437 -
8.438 - else:
8.439 - raise NotImplementedError, augassign.node.__class__
8.440 -
8.441 - return result
8.442 -
8.443 - def visitBitand(self, bitand):
8.444 -
8.445 - """
8.446 - Make a subprogram for the 'bitand' node and record its contents inside the
8.447 - subprogram. Convert...
8.448 -
8.449 - Bitand (node)
8.450 - (node)
8.451 - ...
8.452 -
8.453 - ...to:
8.454 -
8.455 - Subprogram -> Conditional (test) -> ReturnFromBlock ...
8.456 - (else) -> Conditional (test) -> ReturnFromBlock ...
8.457 - (else) -> ...
8.458 - """
8.459 -
8.460 - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
8.461 - self.current_subprograms.append(subprogram)
8.462 -
8.463 - # In the subprogram, make instructions which store each operand, test
8.464 - # for each operand's truth status, and if appropriate return from the
8.465 - # subprogram with the value of the operand.
8.466 -
8.467 - last = bitand.nodes[-1]
8.468 - results = nodes = []
8.469 -
8.470 - # Start by storing the first operand.
8.471 -
8.472 - nodes += [
8.473 - StoreTemp(expr=self.dispatch(bitand.nodes[0]))
8.474 - ]
8.475 -
8.476 - # For viewing purposes, record invocations on the AST node.
8.477 -
8.478 - bitand._ops = []
8.479 -
8.480 - for node in bitand.nodes[1:]:
8.481 -
8.482 - # Make a new AST-style node to wrap the operation program nodes.
8.483 -
8.484 - new_op = Op("&", node)
8.485 - bitand._ops.append(new_op)
8.486 -
8.487 - # Generate the operation involving the previous result and the
8.488 - # current operand.
8.489 -
8.490 - expr = self._visitBinaryOp(new_op, LoadTemp(), self.dispatch(node), "__and__", "__rand__")
8.491 -
8.492 - # Return from the subprogram where the test is not satisfied.
8.493 -
8.494 - if node is not last:
8.495 - nodes += [
8.496 - StoreTemp(expr=expr),
8.497 - Conditional(
8.498 - test=self._visitNot(LoadTemp()),
8.499 - body=[
8.500 - ReturnFromBlock(
8.501 - expr=LoadTemp()
8.502 - )
8.503 - ],
8.504 - else_=[
8.505 - # Subsequent operations go here!
8.506 - ]
8.507 - )
8.508 - ]
8.509 -
8.510 - # Put subsequent operations in the else section of this conditional.
8.511 -
8.512 - nodes = nodes[-1].else_
8.513 -
8.514 - # For the last operation, return the result.
8.515 -
8.516 - else:
8.517 - nodes.append(ReturnFromBlock(expr=expr))
8.518 -
8.519 - # Finish the subprogram definition.
8.520 -
8.521 - subprogram.code = results
8.522 -
8.523 - self.current_subprograms.pop()
8.524 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.525 -
8.526 - # Make an invocation of the subprogram.
8.527 -
8.528 - result = InvokeRef(bitand, 1, produces_result=1, ref=subprogram)
8.529 - return result
8.530 -
8.531 - def visitBreak(self, break_):
8.532 - result = ReturnFromBlock(break_, 1)
8.533 - return result
8.534 -
8.535 - def visitCallFunc(self, callfunc):
8.536 - result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args))
8.537 - if callfunc.star_args is not None:
8.538 - result.star = self.dispatch(callfunc.star_args)
8.539 - if callfunc.dstar_args is not None:
8.540 - result.dstar = self.dispatch(callfunc.dstar_args)
8.541 - result.expr = self.dispatch(callfunc.node)
8.542 - return result
8.543 -
8.544 - def visitClass(self, class_):
8.545 -
8.546 - # Add "object" if the class is not "object" and has an empty bases list.
8.547 -
8.548 - if class_.name != "object" and not class_.bases:
8.549 - bases = [compiler.ast.Name("object")]
8.550 - else:
8.551 - bases = class_.bases
8.552 -
8.553 - structure = Class(name=class_.name, module=self.module, bases=self.dispatches(bases))
8.554 - self.structures.append(structure)
8.555 - within_class = self.within_class
8.556 - self.within_class = 1
8.557 -
8.558 - # Make a subprogram which initialises the class structure.
8.559 -
8.560 - subprogram = Subprogram(name=None, module=self.module, structure=structure, params=[], star=None, dstar=None)
8.561 - self.current_subprograms.append(subprogram)
8.562 - self.current_structures.append(structure) # mostly for name construction
8.563 -
8.564 - # The class is initialised using the code found inside.
8.565 -
8.566 - subprogram.code = self.dispatch(class_.code) + [ReturnFromBlock()]
8.567 -
8.568 - self.within_class = within_class
8.569 - self.current_structures.pop()
8.570 - self.current_subprograms.pop()
8.571 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.572 -
8.573 - # Make a definition of the class associating it with a name.
8.574 -
8.575 - result = Assign(
8.576 - code=[
8.577 - StoreName(class_, 1, # defines the class
8.578 - name=class_.name,
8.579 - expr=LoadRef(ref=structure)
8.580 - ),
8.581 - InvokeRef(
8.582 - class_,
8.583 - share_locals=0, # override the local sharing usually in InvokeRef
8.584 - ref=subprogram
8.585 - )
8.586 - ]
8.587 - )
8.588 - return result
8.589 -
8.590 - comparison_methods = {
8.591 - "==" : ("__eq__", "__ne__"),
8.592 - "!=" : ("__ne__", "__eq__"),
8.593 - "<" : ("__lt__", "__gt__"),
8.594 - "<=" : ("__le__", "__ge__"),
8.595 - ">=" : ("__ge__", "__le__"),
8.596 - ">" : ("__gt__", "__lt__"),
8.597 - "is" : None,
8.598 - "is not" : None,
8.599 - "in" : None,
8.600 - "not in" : None
8.601 - }
8.602 -
8.603 - def visitCompare(self, compare):
8.604 -
8.605 - """
8.606 - Make a subprogram for the 'compare' node and record its contents inside
8.607 - the subprogram. Convert...
8.608 -
8.609 - Compare (expr)
8.610 - (name/node)
8.611 - ...
8.612 -
8.613 - ...to:
8.614 -
8.615 - InvokeRef -> Subprogram -> Conditional (test) -> (body)
8.616 - (else) -> Conditional (test) -> (body)
8.617 - (else) -> ...
8.618 - """
8.619 -
8.620 - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
8.621 - self.current_subprograms.append(subprogram)
8.622 -
8.623 - # In the subprogram, make instructions which invoke a method on the
8.624 - # first operand of each operand pair and, if appropriate, return with
8.625 - # the value from that method.
8.626 -
8.627 - last = compare.ops[-1]
8.628 - previous = self.dispatch(compare.expr)
8.629 - results = nodes = []
8.630 -
8.631 - # For viewing purposes, record invocations on the AST node.
8.632 -
8.633 - compare._ops = []
8.634 -
8.635 - for op in compare.ops:
8.636 - op_name, node = op
8.637 -
8.638 - # Make a new AST-style node to wrap the operation program nodes.
8.639 -
8.640 - new_op = Op(op_name, node)
8.641 - compare._ops.append(new_op)
8.642 -
8.643 - expr = self.dispatch(node)
8.644 -
8.645 - # Identify the operation and produce the appropriate method call.
8.646 -
8.647 - method_names = self.comparison_methods[op_name]
8.648 - if method_names:
8.649 - first_name, alternative_name = method_names
8.650 - invocation = self._visitBinaryCompareOp(new_op, previous, expr, first_name, alternative_name)
8.651 -
8.652 - elif op_name == "is":
8.653 - invocation = InvokeFunction(
8.654 - new_op, 1,
8.655 - expr=LoadName(name="__is__"),
8.656 - args=[previous, expr],
8.657 - star=None,
8.658 - dstar=None)
8.659 -
8.660 - elif op_name == "is not":
8.661 - invocation = Not(
8.662 - new_op, 1,
8.663 - expr=InvokeFunction(
8.664 - new_op,
8.665 - expr=LoadName(name="__is__"),
8.666 - args=[previous, expr],
8.667 - star=None,
8.668 - dstar=None)
8.669 - )
8.670 -
8.671 - elif op_name == "in":
8.672 - invocation = InvokeFunction(
8.673 - new_op, 1,
8.674 - expr=LoadAttr(
8.675 - expr=previous,
8.676 - name="__contains__"
8.677 - ),
8.678 - args=[expr],
8.679 - star=None,
8.680 - dstar=None)
8.681 -
8.682 - elif op_name == "not in":
8.683 - invocation = Not(
8.684 - new_op, 1,
8.685 - expr=InvokeFunction(
8.686 - new_op,
8.687 - expr=LoadAttr(
8.688 - expr=previous,
8.689 - name="__contains__"
8.690 - ),
8.691 - args=[expr],
8.692 - star=None,
8.693 - dstar=None)
8.694 - )
8.695 -
8.696 - else:
8.697 - raise NotImplementedError, op_name
8.698 -
8.699 - nodes.append(StoreTemp(expr=invocation))
8.700 -
8.701 - # Return from the subprogram where the test is not satisfied.
8.702 -
8.703 - if op is not last:
8.704 - nodes.append(
8.705 - Conditional(
8.706 - test=self._visitNot(LoadTemp()),
8.707 - body=[
8.708 - ReturnFromBlock(expr=LoadTemp())
8.709 - ],
8.710 - else_=[
8.711 - ReleaseTemp()
8.712 - # Subsequent operations go here!
8.713 - ]
8.714 - )
8.715 - )
8.716 -
8.717 - # Put subsequent operations in the else section of this conditional.
8.718 -
8.719 - nodes = nodes[-1].else_
8.720 -
8.721 - # For the last operation, return the result.
8.722 -
8.723 - else:
8.724 - nodes.append(
8.725 - ReturnFromBlock(expr=LoadTemp(release=1))
8.726 - )
8.727 -
8.728 - previous = expr
8.729 -
8.730 - # Finish the subprogram definition.
8.731 -
8.732 - subprogram.code = results
8.733 -
8.734 - self.current_subprograms.pop()
8.735 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.736 -
8.737 - # Make an invocation of the subprogram.
8.738 -
8.739 - result = InvokeRef(compare, 1, produces_result=1, ref=subprogram)
8.740 - return result
8.741 -
8.742 - def visitConst(self, const):
8.743 - key = "%s-%s" % (const.value.__class__.__name__, const.value)
8.744 - if not self.constants.has_key(key):
8.745 - self.constants[key] = Constant(name=repr(const.value), value=const.value)
8.746 - result = InvokeFunction(const, 1, expr=LoadName(name=self.constants[key].typename))
8.747 - return result
8.748 -
8.749 - def visitContinue(self, continue_):
8.750 - result = InvokeRef(continue_, 1, ref=self.current_subprograms[-1])
8.751 - return result
8.752 -
8.753 - def visitDict(self, dict):
8.754 - result = InvokeFunction(dict, 1, expr=LoadName(name="dict"))
8.755 - args = []
8.756 - for key, value in dict.items:
8.757 - tuple = InvokeFunction(dict, expr=LoadName(name="tuple"))
8.758 - tuple.set_args([self.dispatch(key), self.dispatch(value)])
8.759 - args.append(tuple)
8.760 - result.set_args(args)
8.761 - return result
8.762 -
8.763 - def visitDiscard(self, discard):
8.764 - return self.dispatch(discard.expr)
8.765 -
8.766 - def visitDiv(self, div):
8.767 - return self._visitBinary(div, "__div__", "__rdiv__")
8.768 -
8.769 - def visitFloorDiv(self, floordiv):
8.770 - return self._visitBinary(floordiv, "__floordiv__", "__rfloordiv__")
8.771 -
8.772 - def visitFor(self, for_):
8.773 -
8.774 - """
8.775 - Make a subprogram for the 'for_' node and record its contents inside the
8.776 - subprogram. Convert...
8.777 -
8.778 - For (assign)
8.779 - (body)
8.780 - (else)
8.781 -
8.782 - ...to:
8.783 -
8.784 - Assign (assign #1)
8.785 - Invoke -> Subprogram -> Try (body) -> (assign #2)
8.786 - (body)
8.787 - Invoke subprogram
8.788 - (handler) -> ...
8.789 - (else) -> ...
8.790 - """
8.791 -
8.792 - return self._visitFor(for_, self.dispatches(for_.body), for_.else_)
8.793 -
8.794 - def _visitFor(self, node, body_stmt, else_=None):
8.795 -
8.796 - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[])
8.797 - self.current_subprograms.append(subprogram)
8.798 -
8.799 - # Always return from conditional sections/subprograms.
8.800 -
8.801 - if else_ is not None:
8.802 - else_stmt = self.dispatch(else_) + [ReturnFromBlock()]
8.803 - else:
8.804 - else_stmt = [ReturnFromBlock()]
8.805 -
8.806 - # Wrap the assignment in a try...except statement.
8.807 - # Inside the body, add a recursive invocation to the subprogram.
8.808 -
8.809 - subprogram.code = [
8.810 - Try(
8.811 - body=[
8.812 - Assign(
8.813 - code=[
8.814 - StoreTemp(
8.815 - expr=InvokeFunction(node,
8.816 - expr=LoadAttr(
8.817 - expr=LoadTemp(),
8.818 - name="next"
8.819 - )
8.820 - )
8.821 - ),
8.822 - self.dispatch(node.assign),
8.823 - ReleaseTemp()
8.824 - ])
8.825 - ] + body_stmt + [
8.826 - InvokeRef(
8.827 - node,
8.828 - ref=subprogram
8.829 - )
8.830 - ],
8.831 - handler=[
8.832 - Conditional(
8.833 - test=InvokeFunction(
8.834 - node,
8.835 - expr=LoadName(name="isinstance"),
8.836 - args=[LoadExc(), LoadName(name="StopIteration")],
8.837 - star=None,
8.838 - dstar=None),
8.839 - body=else_stmt,
8.840 - else_=[
8.841 - Raise(
8.842 - expr=LoadExc()
8.843 - )
8.844 - ]
8.845 - )
8.846 - ],
8.847 - else_=[],
8.848 - finally_=[]
8.849 - ),
8.850 - ReturnFromBlock()
8.851 - ]
8.852 -
8.853 - # Finish the subprogram definition.
8.854 -
8.855 - self.current_subprograms.pop()
8.856 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.857 -
8.858 - # Obtain an iterator for the sequence involved.
8.859 - # Then, make an invocation of the subprogram.
8.860 -
8.861 - result = Assign(node, 1,
8.862 - code=[
8.863 - StoreTemp(
8.864 - expr=InvokeFunction(
8.865 - node,
8.866 - expr=LoadAttr(
8.867 - name="__iter__",
8.868 - expr=self.dispatch(node.list)
8.869 - )
8.870 - )
8.871 - ),
8.872 - InvokeRef(node, ref=subprogram),
8.873 - ReleaseTemp()
8.874 - ]
8.875 - )
8.876 -
8.877 - # Make nice annotations for the viewer.
8.878 -
8.879 - node._iter_call = result.code[0].expr
8.880 - node._next_call = subprogram.code[0].body[0].code[0].expr
8.881 -
8.882 - return result
8.883 -
8.884 - def visitFrom(self, from_):
8.885 - result = Assign(from_, 1)
8.886 - code = []
8.887 - _names = []
8.888 - code.append(
8.889 - StoreTemp(
8.890 - expr=Import(name=from_.modname, alias=1)
8.891 - )
8.892 - )
8.893 - from_._modname = code[-1].expr
8.894 - for name, alias in from_.names:
8.895 - code.append(
8.896 - StoreName(
8.897 - expr=LoadAttr(
8.898 - expr=LoadTemp(),
8.899 - name=name),
8.900 - name=(alias or name)
8.901 - )
8.902 - )
8.903 - _names.append(code[-1].expr)
8.904 - code.append(ReleaseTemp())
8.905 - result.code = code
8.906 - from_._names = _names
8.907 - return result
8.908 -
8.909 - def _visitFunction(self, function, subprogram):
8.910 -
8.911 - """
8.912 - A common function generator which transforms the given 'function' node
8.913 - and initialises the given 'subprogram' appropriately.
8.914 - """
8.915 -
8.916 - # Discover star and dstar parameters.
8.917 -
8.918 - if function.flags & 4 != 0:
8.919 - has_star = 1
8.920 - else:
8.921 - has_star = 0
8.922 - if function.flags & 8 != 0:
8.923 - has_dstar = 1
8.924 - else:
8.925 - has_dstar = 0
8.926 -
8.927 - # Discover the number of defaults and positional parameters.
8.928 -
8.929 - ndefaults = len(function.defaults)
8.930 - npositional = len(function.argnames) - has_star - has_dstar
8.931 -
8.932 - # Produce star and dstar parameters with appropriate defaults.
8.933 -
8.934 - if has_star:
8.935 - star = (
8.936 - function.argnames[npositional],
8.937 - self.dispatch(compiler.ast.List([]))
8.938 - )
8.939 - else:
8.940 - star = None
8.941 - if has_dstar:
8.942 - dstar = (
8.943 - function.argnames[npositional + has_star],
8.944 - self.dispatch(compiler.ast.Dict([]))
8.945 - )
8.946 - else:
8.947 - dstar = None
8.948 -
8.949 - params = []
8.950 - for i in range(0, npositional - ndefaults):
8.951 - params.append((function.argnames[i], None))
8.952 -
8.953 - # Process defaults.
8.954 -
8.955 - for i in range(0, ndefaults):
8.956 - default = function.defaults[i]
8.957 - if default is not None:
8.958 - params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default)))
8.959 - else:
8.960 - params.append((function.argnames[npositional - ndefaults + i], None))
8.961 -
8.962 - subprogram.params = params
8.963 - subprogram.star = star
8.964 - subprogram.dstar = dstar
8.965 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.966 -
8.967 - def visitFunction(self, function):
8.968 -
8.969 - """
8.970 - Make a subprogram for the 'function' and record it outside the main
8.971 - tree. Produce something like the following:
8.972 -
8.973 - StoreName (name)
8.974 - (expr) -> LoadRef (ref) -> Subprogram (params)
8.975 - (star)
8.976 - (dstar)
8.977 - """
8.978 -
8.979 - subprogram = Subprogram(function, name=function.name, module=self.module, structures=self.current_structures[:],
8.980 - internal=0, returns_value=1, star=None, dstar=None, is_method=self.within_class, original_def=function)
8.981 -
8.982 - # Make nice annotations for the viewer.
8.983 -
8.984 - function._subprogram = subprogram
8.985 -
8.986 - self.current_subprograms.append(subprogram)
8.987 - within_class = self.within_class
8.988 - self.within_class = 0
8.989 -
8.990 - subprogram.code = self.dispatch(function.code) + [ReturnFromFunction()]
8.991 -
8.992 - self.within_class = within_class
8.993 - self.current_subprograms.pop()
8.994 - self._visitFunction(function, subprogram)
8.995 -
8.996 - # Make a definition of the function associating it with a name.
8.997 -
8.998 - result = StoreName(function, 1, name=function.name, expr=LoadRef(ref=subprogram))
8.999 - return result
8.1000 -
8.1001 - def visitGetattr(self, getattr):
8.1002 - result = LoadAttr(getattr, 1,
8.1003 - name=getattr.attrname,
8.1004 - expr=self.dispatch(getattr.expr)
8.1005 - )
8.1006 - return result
8.1007 -
8.1008 - def visitGlobal(self, global_):
8.1009 - result = Global(global_, 1,
8.1010 - names=global_.names
8.1011 - )
8.1012 - return result
8.1013 -
8.1014 - def visitIf(self, if_):
8.1015 -
8.1016 - """
8.1017 - Make conditionals for each test from an 'if_' AST node, adding the body
8.1018 - and putting each subsequent test as part of the conditional's else
8.1019 - section.
8.1020 -
8.1021 - Convert...
8.1022 -
8.1023 - If (test/body)
8.1024 - (test/body)
8.1025 - ...
8.1026 - (else/body)
8.1027 -
8.1028 - ...to:
8.1029 -
8.1030 - Conditional (test) -> (body)
8.1031 - (else) -> Conditional (test) -> (body)
8.1032 - (else) -> ...
8.1033 - """
8.1034 -
8.1035 -
8.1036 - results = nodes = []
8.1037 -
8.1038 - # Produce something like...
8.1039 - # expr.__bool__() ? body
8.1040 -
8.1041 - first = 1
8.1042 - for compare, stmt in if_.tests:
8.1043 -
8.1044 - # Set the first as the defining node.
8.1045 -
8.1046 - test = Conditional(if_, first,
8.1047 - test=InvokeFunction(
8.1048 - if_,
8.1049 - expr=LoadAttr(
8.1050 - expr=self.dispatch(compare),
8.1051 - name="__bool__"
8.1052 - ),
8.1053 - )
8.1054 - )
8.1055 - test.body = self.dispatch(stmt)
8.1056 - nodes.append(test)
8.1057 - nodes = test.else_ = []
8.1058 - first = 0
8.1059 -
8.1060 - # Add the compound statement from any else clause to the end.
8.1061 -
8.1062 - if if_.else_ is not None:
8.1063 - nodes += self.dispatch(if_.else_)
8.1064 -
8.1065 - result = results[0]
8.1066 - return result
8.1067 -
8.1068 - def visitImport(self, import_):
8.1069 - result = Assign(import_, 1)
8.1070 - code = []
8.1071 - _names = []
8.1072 - for path, alias in import_.names:
8.1073 - importer = Import(name=path, alias=alias)
8.1074 - top = alias or path.split(".")[0]
8.1075 - code.append(StoreName(expr=importer, name=top))
8.1076 - _names.append(code[-1].expr)
8.1077 - result.code = code
8.1078 - import_._names = _names
8.1079 - return result
8.1080 -
8.1081 - def visitInvert(self, invert):
8.1082 - return self._visitUnary(invert, "__invert__")
8.1083 -
8.1084 - def visitKeyword(self, keyword):
8.1085 - result = Keyword(keyword, 1,
8.1086 - name=keyword.name,
8.1087 - expr=self.dispatch(keyword.expr)
8.1088 - )
8.1089 - return result
8.1090 -
8.1091 - def visitLambda(self, lambda_):
8.1092 -
8.1093 - # Make a subprogram for the function and record it outside the main
8.1094 - # tree.
8.1095 -
8.1096 - subprogram = Subprogram(lambda_, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=lambda_)
8.1097 -
8.1098 - # Make nice annotations for the viewer.
8.1099 -
8.1100 - lambda_._subprogram = subprogram
8.1101 -
8.1102 - # Process the lambda contents.
8.1103 -
8.1104 - self.current_subprograms.append(subprogram)
8.1105 - subprogram.code = [ReturnFromFunction(expr=self.dispatch(lambda_.code))]
8.1106 - self.current_subprograms.pop()
8.1107 - self._visitFunction(lambda_, subprogram)
8.1108 -
8.1109 - # Get the subprogram reference to the lambda.
8.1110 -
8.1111 - return LoadRef(lambda_, 1, ref=subprogram)
8.1112 -
8.1113 - def visitList(self, list):
8.1114 -
8.1115 - # Make a subprogram for the list construction and record it outside the
8.1116 - # main tree.
8.1117 -
8.1118 - subprogram = Subprogram(list, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=list)
8.1119 - self.current_subprograms.append(subprogram)
8.1120 -
8.1121 - # Make nice annotations for the viewer.
8.1122 -
8.1123 - list._subprogram = subprogram
8.1124 -
8.1125 - subprogram.code=[
8.1126 - StoreTemp(
8.1127 - expr=InvokeFunction(
8.1128 - list,
8.1129 - expr=LoadName(
8.1130 - name="list"
8.1131 - ),
8.1132 - args=[],
8.1133 - star=None,
8.1134 - dstar=None
8.1135 - )
8.1136 - )
8.1137 - ]
8.1138 -
8.1139 - for node in list.nodes:
8.1140 - subprogram.code.append(
8.1141 - InvokeFunction(
8.1142 - list,
8.1143 - expr=LoadAttr(
8.1144 - expr=LoadTemp(),
8.1145 - name="append"
8.1146 - ),
8.1147 - args=[self.dispatch(node)],
8.1148 - star=None,
8.1149 - dstar=None
8.1150 - )
8.1151 - )
8.1152 -
8.1153 - subprogram.code.append(
8.1154 - ReturnFromBlock(
8.1155 - expr=LoadTemp(release=1)
8.1156 - )
8.1157 - )
8.1158 -
8.1159 - self.current_subprograms.pop()
8.1160 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.1161 -
8.1162 - # Make an invocation of the subprogram.
8.1163 -
8.1164 - result = InvokeRef(list, 1,
8.1165 - produces_result=1,
8.1166 - ref=subprogram
8.1167 - )
8.1168 - return result
8.1169 -
8.1170 - def visitListComp(self, listcomp):
8.1171 -
8.1172 - # Make a subprogram for the list comprehension and record it outside the
8.1173 - # main tree.
8.1174 -
8.1175 - subprogram = Subprogram(listcomp, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=listcomp)
8.1176 - self.current_subprograms.append(subprogram)
8.1177 -
8.1178 - # Make nice annotations for the viewer.
8.1179 -
8.1180 - listcomp._subprogram = subprogram
8.1181 -
8.1182 - # Add a temporary variable.
8.1183 - # Produce for loops within the subprogram.
8.1184 - # Return the result.
8.1185 -
8.1186 - subprogram.code = [
8.1187 - StoreTemp(
8.1188 - index="listcomp",
8.1189 - expr=InvokeFunction(
8.1190 - expr=LoadName(name="list"),
8.1191 - args=[],
8.1192 - star=None,
8.1193 - dstar=None
8.1194 - )
8.1195 - )
8.1196 - ] + self._visitListCompFor(listcomp, listcomp.quals) + [
8.1197 - ReturnFromBlock(
8.1198 - expr=LoadTemp(
8.1199 - index="listcomp",
8.1200 - release=1
8.1201 - )
8.1202 - )
8.1203 - ]
8.1204 -
8.1205 - self.current_subprograms.pop()
8.1206 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.1207 -
8.1208 - # Make an invocation of the subprogram.
8.1209 -
8.1210 - result = InvokeRef(listcomp, 1,
8.1211 - produces_result=1,
8.1212 - ref=subprogram
8.1213 - )
8.1214 - return result
8.1215 -
8.1216 - def _visitListCompFor(self, node, quals):
8.1217 - qual = quals[0]
8.1218 - if len(quals) > 1:
8.1219 - body = self._visitListCompFor(node, quals[1:])
8.1220 - if qual.ifs:
8.1221 - body = self._visitListCompIf(node, qual.ifs, body)
8.1222 - elif qual.ifs:
8.1223 - body = self._visitListCompIf(node, qual.ifs)
8.1224 - else:
8.1225 - body = self._visitListCompBody(node)
8.1226 - return [self._visitFor(qual, body)]
8.1227 -
8.1228 - def _visitListCompIf(self, node, ifs, expr=None):
8.1229 - if_ = ifs[0]
8.1230 - if len(ifs) > 1:
8.1231 - body = self._visitListCompIf(node, ifs[1:], expr)
8.1232 - elif expr is None:
8.1233 - body = self._visitListCompBody(node)
8.1234 - else:
8.1235 - body = expr
8.1236 - return [
8.1237 - Conditional(if_, 1,
8.1238 - test=InvokeFunction(
8.1239 - if_,
8.1240 - expr=LoadAttr(
8.1241 - expr=self.dispatch(if_.test),
8.1242 - name="__bool__"
8.1243 - ),
8.1244 - ),
8.1245 - body=body,
8.1246 - else_=[]
8.1247 - )
8.1248 - ]
8.1249 -
8.1250 - def _visitListCompBody(self, node):
8.1251 - return [
8.1252 - InvokeFunction(
8.1253 - expr=LoadAttr(
8.1254 - expr=LoadTemp(index="listcomp"),
8.1255 - name="append"
8.1256 - ),
8.1257 - args=[self.dispatch(node.expr)],
8.1258 - star=None,
8.1259 - dstar=None
8.1260 - )
8.1261 - ]
8.1262 -
8.1263 - def visitMod(self, mod):
8.1264 - return self._visitBinary(mod, "__mod__", "__rmod__")
8.1265 -
8.1266 - def visitMul(self, mul):
8.1267 - return self._visitBinary(mul, "__mul__", "__rmul__")
8.1268 -
8.1269 - def visitName(self, name):
8.1270 - result = LoadName(name, 1, name=name.name)
8.1271 - return result
8.1272 -
8.1273 - def _visitNot(self, expr, not_=None):
8.1274 - invocation = InvokeFunction(
8.1275 - not_, # NOTE: May need a real original node.
8.1276 - expr=LoadAttr(
8.1277 - expr=expr,
8.1278 - name="__bool__"
8.1279 - ),
8.1280 - )
8.1281 - if not_ is not None:
8.1282 - result = Not(not_, 1, expr=invocation)
8.1283 - else:
8.1284 - result = Not(expr=invocation)
8.1285 - return result
8.1286 -
8.1287 - def visitNot(self, not_):
8.1288 - return self._visitNot(self.dispatch(not_.expr), not_)
8.1289 -
8.1290 - def visitOr(self, or_):
8.1291 -
8.1292 - """
8.1293 - Make a subprogram for the 'or_' node and record its contents inside the
8.1294 - subprogram. Convert...
8.1295 -
8.1296 - Or (test)
8.1297 - (test)
8.1298 - ...
8.1299 -
8.1300 - ...to:
8.1301 -
8.1302 - Subprogram -> Conditional (test) -> ReturnFromBlock ...
8.1303 - (else) -> Conditional (test) -> ReturnFromBlock ...
8.1304 - (else) -> ...
8.1305 - """
8.1306 -
8.1307 - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
8.1308 - self.current_subprograms.append(subprogram)
8.1309 -
8.1310 - # In the subprogram, make instructions which store each operand, test
8.1311 - # for each operand's truth status, and if appropriate return from the
8.1312 - # subprogram with the value of the operand.
8.1313 -
8.1314 - last = or_.nodes[-1]
8.1315 - results = nodes = []
8.1316 -
8.1317 - for node in or_.nodes:
8.1318 - expr = self.dispatch(node)
8.1319 -
8.1320 - # Return from the subprogram where the test is satisfied.
8.1321 -
8.1322 - if node is not last:
8.1323 - nodes.append(StoreTemp(expr=expr))
8.1324 - invocation = InvokeFunction(or_, expr=LoadAttr(expr=LoadTemp(), name="__bool__"))
8.1325 - test = Conditional(test=invocation, body=[ReturnFromBlock(expr=LoadTemp())])
8.1326 - nodes.append(test)
8.1327 -
8.1328 - # Put subsequent operations in the else section of this conditional.
8.1329 -
8.1330 - nodes = test.else_ = [ReleaseTemp()]
8.1331 -
8.1332 - # For the last operation, return the result.
8.1333 -
8.1334 - else:
8.1335 - nodes.append(
8.1336 - ReturnFromBlock(expr=expr)
8.1337 - )
8.1338 -
8.1339 - # Finish the subprogram definition.
8.1340 -
8.1341 - subprogram.code = results
8.1342 -
8.1343 - self.current_subprograms.pop()
8.1344 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.1345 -
8.1346 - # Make an invocation of the subprogram.
8.1347 -
8.1348 - result = InvokeRef(or_, 1,
8.1349 - produces_result=1,
8.1350 - ref=subprogram
8.1351 - )
8.1352 - return result
8.1353 -
8.1354 - def visitPass(self, pass_):
8.1355 - return Pass(pass_, 1)
8.1356 -
8.1357 - def visitPower(self, power):
8.1358 - return self._visitBinary(power, "__pow__", "__rpow__")
8.1359 -
8.1360 - def visitPrint(self, print_):
8.1361 -
8.1362 - """
8.1363 - Convert...
8.1364 -
8.1365 - Print (dest) ->
8.1366 - (nodes)
8.1367 -
8.1368 - ...to:
8.1369 -
8.1370 - StoreTemp (index) -> "print"
8.1371 - (expr) -> LoadAttr (expr) -> (dest)
8.1372 - (name) -> "write"
8.1373 - InvokeFunction (expr) -> LoadTemp (index) -> "print"
8.1374 - (args) -> [(node)]
8.1375 - ReleaseTemp (index) -> "print"
8.1376 - """
8.1377 -
8.1378 - if print_.dest is not None:
8.1379 - dest = self.dispatch(print_.dest)
8.1380 - else:
8.1381 - dest = self.dispatch(compiler.ast.Name("stdout"))
8.1382 -
8.1383 - result = Assign(print_, 1,
8.1384 - code=[
8.1385 - StoreTemp(
8.1386 - index="print",
8.1387 - expr=LoadAttr(
8.1388 - expr=dest,
8.1389 - name="write"
8.1390 - )
8.1391 - )
8.1392 - ]
8.1393 - )
8.1394 -
8.1395 - for node in print_.nodes:
8.1396 - result.code.append(
8.1397 - InvokeFunction(
8.1398 - print_,
8.1399 - expr=LoadTemp(index="print"),
8.1400 - args=[self.dispatch(node)],
8.1401 - star=None,
8.1402 - dstar=None
8.1403 - )
8.1404 - )
8.1405 -
8.1406 - result.code.append(
8.1407 - ReleaseTemp(index="print")
8.1408 - )
8.1409 -
8.1410 - return result
8.1411 -
8.1412 - def visitPrintnl(self, printnl):
8.1413 - result = self.visitPrint(printnl)
8.1414 - result.code.insert(
8.1415 - len(result.code) - 1,
8.1416 - InvokeFunction(
8.1417 - printnl,
8.1418 - expr=LoadTemp(index="print"),
8.1419 - args=[self.dispatch(compiler.ast.Const("\n"))],
8.1420 - star=None,
8.1421 - dstar=None
8.1422 - )
8.1423 - )
8.1424 - return result
8.1425 -
8.1426 - def visitRaise(self, raise_):
8.1427 - result = Raise(raise_, 1)
8.1428 - if raise_.expr2 is None:
8.1429 - result.expr = self.dispatch(raise_.expr1)
8.1430 - else:
8.1431 - result.expr = InvokeFunction(
8.1432 - raise_,
8.1433 - expr=self.dispatch(raise_.expr1),
8.1434 - args=[self.dispatch(raise_.expr2)],
8.1435 - star=None,
8.1436 - dstar=None
8.1437 - )
8.1438 - if raise_.expr3 is not None:
8.1439 - result.traceback = self.dispatch(raise_.expr3)
8.1440 - else:
8.1441 - result.traceback = None
8.1442 - return result
8.1443 -
8.1444 - def visitReturn(self, return_):
8.1445 - result = ReturnFromFunction(return_, 1,
8.1446 - expr=self.dispatch(return_.value)
8.1447 - )
8.1448 - return result
8.1449 -
8.1450 - def _visitSlice(self, slice, expr, lower, upper, flags, value=None):
8.1451 - if flags == "OP_ASSIGN":
8.1452 - result = InvokeFunction(slice, 1,
8.1453 - expr=LoadAttr(
8.1454 - expr=expr,
8.1455 - name="__setslice__"
8.1456 - ),
8.1457 - star=None,
8.1458 - dstar=None,
8.1459 - args=[lower, upper, value]
8.1460 - )
8.1461 - elif flags == "OP_APPLY":
8.1462 - args = []
8.1463 - result = InvokeFunction(slice, 1,
8.1464 - expr=LoadAttr(
8.1465 - expr=expr,
8.1466 - name="__getslice__"
8.1467 - ),
8.1468 - star=None,
8.1469 - dstar=None,
8.1470 - args=[lower, upper]
8.1471 - )
8.1472 - elif flags == "OP_DELETE":
8.1473 - args = []
8.1474 - result = InvokeFunction(slice, 1,
8.1475 - expr=LoadAttr(
8.1476 - expr=expr,
8.1477 - name="__delslice__"
8.1478 - ),
8.1479 - star=None,
8.1480 - dstar=None,
8.1481 - args=[lower, upper]
8.1482 - )
8.1483 - else:
8.1484 - raise NotImplementedError, flags
8.1485 -
8.1486 - return result
8.1487 -
8.1488 - def visitSlice(self, slice, in_sequence=0):
8.1489 - return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower),
8.1490 - self.dispatch_or_none(slice.upper), slice.flags, self._visitAssNameOrAttr(slice, in_sequence))
8.1491 -
8.1492 - def visitSliceobj(self, sliceobj):
8.1493 - return InvokeFunction(sliceobj, 1,
8.1494 - expr=LoadName(name="slice"),
8.1495 - args=self.dispatches(sliceobj.nodes),
8.1496 - star=None,
8.1497 - dstar=None
8.1498 - )
8.1499 -
8.1500 - def visitStmt(self, stmt):
8.1501 - return self.dispatches(stmt.nodes)
8.1502 -
8.1503 - def visitSub(self, sub):
8.1504 - return self._visitBinary(sub, "__sub__", "__rsub__")
8.1505 -
8.1506 - def _visitSubscript(self, subscript, expr, subs, flags, value=None):
8.1507 - if flags == "OP_ASSIGN":
8.1508 - result = InvokeFunction(subscript, 1,
8.1509 - expr=LoadAttr(
8.1510 - expr=expr,
8.1511 - name="__setitem__"
8.1512 - ),
8.1513 - star=None,
8.1514 - dstar=None,
8.1515 - args=[subs, value]
8.1516 - )
8.1517 - elif flags == "OP_APPLY":
8.1518 - args = []
8.1519 - result = InvokeFunction(subscript, 1,
8.1520 - expr=LoadAttr(
8.1521 - expr=expr,
8.1522 - name="__getitem__"
8.1523 - ),
8.1524 - star=None,
8.1525 - dstar=None,
8.1526 - args=[subs]
8.1527 - )
8.1528 - elif flags == "OP_DELETE":
8.1529 - args = []
8.1530 - result = InvokeFunction(subscript, 1,
8.1531 - expr=LoadAttr(
8.1532 - expr=expr,
8.1533 - name="__delitem__"
8.1534 - ),
8.1535 - star=None,
8.1536 - dstar=None,
8.1537 - args=[subs]
8.1538 - )
8.1539 - else:
8.1540 - raise NotImplementedError, flags
8.1541 -
8.1542 - return result
8.1543 -
8.1544 - def _visitSubscriptSubs(self, node, subs):
8.1545 - if len(subs) == 1:
8.1546 - return self.dispatch(subs[0])
8.1547 - else:
8.1548 - return InvokeFunction(node, 1,
8.1549 - expr=LoadName(name="tuple"),
8.1550 - args=self.dispatches(subs),
8.1551 - star=None,
8.1552 - dstar=None
8.1553 - )
8.1554 -
8.1555 - def visitSubscript(self, subscript, in_sequence=0):
8.1556 - return self._visitSubscript(
8.1557 - subscript, self.dispatch(subscript.expr), self._visitSubscriptSubs(subscript, subscript.subs), subscript.flags,
8.1558 - self._visitAssNameOrAttr(subscript, in_sequence)
8.1559 - )
8.1560 -
8.1561 - def visitTryExcept(self, tryexcept):
8.1562 -
8.1563 - """
8.1564 - Make conditionals for each handler associated with a 'tryexcept' node.
8.1565 -
8.1566 - Convert...
8.1567 -
8.1568 - TryExcept (body)
8.1569 - (else)
8.1570 - (spec/assign/stmt)
8.1571 - ...
8.1572 -
8.1573 - ...to:
8.1574 -
8.1575 - Try (body)
8.1576 - (else)
8.1577 - (handler) -> Conditional (test) -> (stmt)
8.1578 - (body) -> ResetExc ...
8.1579 - (else) -> Conditional (test) -> (stmt)
8.1580 - (body) -> ResetExc ...
8.1581 - (else) -> ...
8.1582 - """
8.1583 -
8.1584 - result = Try(tryexcept, 1, body=[], else_=[], finally_=[])
8.1585 -
8.1586 - if tryexcept.body is not None:
8.1587 - result.body = self.dispatch(tryexcept.body)
8.1588 - if tryexcept.else_ is not None:
8.1589 - result.else_ = self.dispatch(tryexcept.else_)
8.1590 -
8.1591 - results = nodes = []
8.1592 - catch_all = 0
8.1593 -
8.1594 - for spec, assign, stmt in tryexcept.handlers:
8.1595 -
8.1596 - # If no specification exists, produce an unconditional block.
8.1597 -
8.1598 - if spec is None:
8.1599 - nodes += self.dispatch(stmt)
8.1600 - catch_all = 1
8.1601 -
8.1602 - # Produce an exception value check.
8.1603 -
8.1604 - else:
8.1605 - test = Conditional(
8.1606 - isolate_test=1,
8.1607 - test=CheckType(expr=LoadExc(), choices=self._visitTryExcept(spec))
8.1608 - )
8.1609 - test.body = []
8.1610 -
8.1611 - if assign is not None:
8.1612 - test.body.append(
8.1613 - Assign(
8.1614 - code=[
8.1615 - StoreTemp(expr=LoadExc()),
8.1616 - self.dispatch(assign),
8.1617 - ReleaseTemp()
8.1618 - ]
8.1619 - )
8.1620 - )
8.1621 -
8.1622 - test.body += [ResetExc()] + self.dispatch(stmt)
8.1623 - nodes.append(test)
8.1624 - nodes = test.else_ = []
8.1625 -
8.1626 - # Add a raise operation to deal with unhandled exceptions.
8.1627 -
8.1628 - if not catch_all:
8.1629 - nodes.append(
8.1630 - Raise(
8.1631 - expr=LoadExc())
8.1632 - )
8.1633 -
8.1634 - result.handler = results
8.1635 - return result
8.1636 -
8.1637 - def _visitTryExcept(self, spec):
8.1638 -
8.1639 - "Return a list of nodes for the given exception type 'spec'."
8.1640 -
8.1641 - if isinstance(spec, compiler.ast.Tuple):
8.1642 - nodes = []
8.1643 - for node in spec.nodes:
8.1644 - nodes += self._visitTryExcept(node)
8.1645 - else:
8.1646 - nodes = [self.dispatch(spec)]
8.1647 - return nodes
8.1648 -
8.1649 - def visitTryFinally(self, tryfinally):
8.1650 - result = Try(tryfinally, 1, body=[], else_=[], finally_=[])
8.1651 - if tryfinally.body is not None:
8.1652 - result.body = self.dispatch(tryfinally.body)
8.1653 - if tryfinally.final is not None:
8.1654 - result.finally_ = self.dispatch(tryfinally.final)
8.1655 - return result
8.1656 -
8.1657 - def visitTuple(self, tuple):
8.1658 -
8.1659 - "Make a MakeTuple node containing the original 'tuple' contents."
8.1660 -
8.1661 - result = MakeTuple(tuple, 1,
8.1662 - nodes=self.dispatches(tuple.nodes)
8.1663 - )
8.1664 - return result
8.1665 -
8.1666 - def visitUnaryAdd(self, unaryadd):
8.1667 - return self._visitUnary(unaryadd, "__pos__")
8.1668 -
8.1669 - def visitUnarySub(self, unarysub):
8.1670 - return self._visitUnary(unarysub, "__neg__")
8.1671 -
8.1672 - def visitWhile(self, while_):
8.1673 -
8.1674 - """
8.1675 - Make a subprogram for the 'while' node and record its contents inside the
8.1676 - subprogram. Convert...
8.1677 -
8.1678 - While (test) -> (body)
8.1679 - (else)
8.1680 -
8.1681 - ...to:
8.1682 -
8.1683 - Subprogram -> Conditional (test) -> (body) -> Invoke subprogram
8.1684 - (else) -> Conditional (test) -> ReturnFromBlock ...
8.1685 - (else) -> ...
8.1686 - """
8.1687 -
8.1688 - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None)
8.1689 - self.current_subprograms.append(subprogram)
8.1690 -
8.1691 - # Include a conditional statement in the subprogram.
8.1692 - # Inside the conditional, add a recursive invocation to the subprogram
8.1693 - # if the test condition was satisfied.
8.1694 - # Return within the main section of the loop.
8.1695 -
8.1696 - test = Conditional(
8.1697 - test=InvokeFunction(
8.1698 - while_,
8.1699 - expr=LoadAttr(
8.1700 - expr=self.dispatch(while_.test),
8.1701 - name="__bool__"),
8.1702 - ),
8.1703 - body=self.dispatch(while_.body) + [
8.1704 - InvokeRef(
8.1705 - while_,
8.1706 - ref=subprogram
8.1707 - ),
8.1708 - ReturnFromBlock()
8.1709 - ],
8.1710 - else_=[]
8.1711 - )
8.1712 -
8.1713 - # Provide the else section, if present, along with an explicit return.
8.1714 -
8.1715 - if while_.else_ is not None:
8.1716 - test.else_ = self.dispatch(while_.else_) + [ReturnFromBlock()]
8.1717 -
8.1718 - # Finish the subprogram definition.
8.1719 -
8.1720 - subprogram.code = [test]
8.1721 -
8.1722 - self.current_subprograms.pop()
8.1723 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.1724 -
8.1725 - # Make an invocation of the subprogram.
8.1726 -
8.1727 - result = InvokeRef(while_, 1, ref=subprogram)
8.1728 -
8.1729 - # Make nice annotations for the viewer.
8.1730 -
8.1731 - while_._test_call = subprogram.code[0].test
8.1732 -
8.1733 - return result
8.1734 -
8.1735 - # NOTE: Not actually supported.
8.1736 - # NOTE: Virtually the same as visitReturn...
8.1737 -
8.1738 - def visitYield(self, yield_):
8.1739 - result = Yield(yield_, 1,
8.1740 - expr=self.dispatch(yield_.value)
8.1741 - )
8.1742 - return result
8.1743 -
8.1744 - # Convenience methods.
8.1745 -
8.1746 - def _visitBinary(self, binary, left_name, right_name):
8.1747 - return self._visitBinaryOp(binary, self.dispatch(binary.left), self.dispatch(binary.right), left_name, right_name)
8.1748 -
8.1749 - def _visitBinaryCompareOp(self, binary, left, right, left_name, right_name):
8.1750 -
8.1751 - """
8.1752 - Emulate the current mechanisms by producing nodes as follows:
8.1753 -
8.1754 - InvokeRef -> Subprogram -> StoreTemp (expr) -> x.__lt__(y)
8.1755 - Conditional (test) -> __is__(LoadTemp, NotImplemented)
8.1756 - (body) -> ReleaseTemp
8.1757 - StoreTemp (expr) -> y.__gt__(x)
8.1758 - Conditional (test) -> __is__(LoadTemp, NotImplemented)
8.1759 - (body) -> ReturnFromBlock (expr) -> False
8.1760 - (else) -> ReturnFromBlock (expr) -> LoadTemp
8.1761 - (else) -> ReturnFromBlock (expr) -> LoadTemp
8.1762 - """
8.1763 -
8.1764 - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
8.1765 - self.current_subprograms.append(subprogram)
8.1766 -
8.1767 - subprogram.code = [
8.1768 - StoreTemp(
8.1769 - expr=InvokeFunction(
8.1770 - binary,
8.1771 - expr=LoadAttr(expr=left, name=left_name),
8.1772 - args=[right],
8.1773 - star=None,
8.1774 - dstar=None)
8.1775 - ),
8.1776 - Conditional(
8.1777 - isolate_test=1,
8.1778 - test=CheckType(
8.1779 - expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")]
8.1780 - ),
8.1781 - body=[
8.1782 - ReleaseTemp(),
8.1783 - StoreTemp(
8.1784 - expr=InvokeFunction(
8.1785 - binary,
8.1786 - expr=LoadAttr(expr=right, name=right_name),
8.1787 - args=[left],
8.1788 - star=None,
8.1789 - dstar=None)
8.1790 - ),
8.1791 - Conditional(
8.1792 - isolate_test=1,
8.1793 - test=CheckType(
8.1794 - expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")]
8.1795 - ),
8.1796 - body=[
8.1797 - ReturnFromBlock(
8.1798 - expr=LoadName(name="False")
8.1799 - )
8.1800 - ],
8.1801 - else_=[
8.1802 - CheckType(
8.1803 - inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")]
8.1804 - ),
8.1805 - ReturnFromBlock(
8.1806 - expr=LoadTemp()
8.1807 - )
8.1808 - ]
8.1809 - )
8.1810 - ],
8.1811 - else_=[
8.1812 - CheckType(
8.1813 - inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")]
8.1814 - ),
8.1815 - ReturnFromBlock(
8.1816 - expr=LoadTemp()
8.1817 - )
8.1818 - ]
8.1819 - )
8.1820 - ]
8.1821 -
8.1822 - self.current_subprograms.pop()
8.1823 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.1824 -
8.1825 - result = InvokeRef(
8.1826 - binary,
8.1827 - produces_result=1,
8.1828 - ref=subprogram
8.1829 - )
8.1830 -
8.1831 - # Make nice annotations for the viewer.
8.1832 -
8.1833 - binary._left_call = subprogram.code[0].expr
8.1834 - binary._right_call = subprogram.code[1].body[1].expr
8.1835 -
8.1836 - return result
8.1837 -
8.1838 - def _visitBinaryOp(self, binary, left, right, left_name, right_name):
8.1839 -
8.1840 - """
8.1841 - Emulate the current mechanisms by producing nodes as follows:
8.1842 -
8.1843 - InvokeRef -> Subprogram -> Try (body) -> ReturnFromBlock (expr) -> x.__add__(y)
8.1844 - (else)
8.1845 - (handler) -> Conditional (test) -> CheckType (expr) -> LoadExc
8.1846 - (choices) -> LoadName TypeError
8.1847 - (body) -> ReturnFromBlock (expr) -> y.__radd__(x)
8.1848 - (else)
8.1849 - """
8.1850 -
8.1851 - subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
8.1852 - self.current_subprograms.append(subprogram)
8.1853 -
8.1854 - subprogram.code = [
8.1855 - Try(binary, 1,
8.1856 - body=[
8.1857 - ReturnFromBlock(
8.1858 - expr=InvokeFunction(
8.1859 - binary,
8.1860 - expr=LoadAttr(expr=left, name=left_name),
8.1861 - args=[right],
8.1862 - star=None,
8.1863 - dstar=None)
8.1864 - )
8.1865 - ],
8.1866 - else_=[],
8.1867 - finally_=[],
8.1868 - handler=[
8.1869 - Conditional(
8.1870 - test=CheckType(expr=LoadExc(), choices=[LoadName(name="TypeError")]),
8.1871 - body=[
8.1872 - ReturnFromBlock(
8.1873 - expr=InvokeFunction(
8.1874 - binary,
8.1875 - expr=LoadAttr(expr=right, name=right_name),
8.1876 - args=[left],
8.1877 - star=None,
8.1878 - dstar=None)
8.1879 - )
8.1880 - ],
8.1881 - else_=[]
8.1882 - )
8.1883 - ]
8.1884 - )
8.1885 - ]
8.1886 -
8.1887 - self.current_subprograms.pop()
8.1888 - self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
8.1889 -
8.1890 - result = InvokeRef(
8.1891 - binary,
8.1892 - produces_result=1,
8.1893 - ref=subprogram
8.1894 - )
8.1895 -
8.1896 - # Make nice annotations for the viewer.
8.1897 -
8.1898 - binary._left_call = subprogram.code[0].body[0].expr
8.1899 - binary._right_call = subprogram.code[0].handler[0].body[0].expr
8.1900 -
8.1901 - return result
8.1902 -
8.1903 - def _visitBuiltin(self, builtin, name):
8.1904 - result = InvokeFunction(builtin, 1, expr=LoadName(name=name), args=self.dispatches(builtin.nodes))
8.1905 - return result
8.1906 -
8.1907 - def _visitUnary(self, unary, name):
8.1908 - result = InvokeFunction(unary, 1,
8.1909 - expr=LoadAttr(
8.1910 - expr=self.dispatch(unary.expr),
8.1911 - name=name
8.1912 - )
8.1913 - )
8.1914 -
8.1915 - # Make nice annotations for the viewer.
8.1916 -
8.1917 - unary._unary_call = result
8.1918 -
8.1919 - return result
8.1920 -
8.1921 -# Convenience functions.
8.1922 -
8.1923 -def simplify(filename, builtins=0, module_name=None):
8.1924 -
8.1925 - """
8.1926 - Simplify the module stored in the file with the given 'filename'.
8.1927 -
8.1928 - If the optional 'builtins' parameter is set to a true value (the default
8.1929 - being a false value), then the module is considered as the builtins module.
8.1930 - """
8.1931 -
8.1932 - simplifier = Simplifier(builtins)
8.1933 - module = compiler.parseFile(filename)
8.1934 - compiler.misc.set_filename(filename, module)
8.1935 - if builtins:
8.1936 - name = module_name or "__builtins__"
8.1937 - else:
8.1938 - path, ext = os.path.splitext(filename)
8.1939 - path, name = os.path.split(path)
8.1940 - name = module_name or name
8.1941 - simplified = simplifier.process(module, name)
8.1942 - return simplified
8.1943 -
8.1944 -# vim: tabstop=4 expandtab shiftwidth=4
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/simplify/__init__.py Sun May 27 18:25:25 2007 +0200
9.3 @@ -0,0 +1,1941 @@
9.4 +#!/usr/bin/env python
9.5 +
9.6 +"""
9.7 +Simplify AST structures for easier type propagation and analysis. The code in
9.8 +this module processes AST trees originating from the compiler module and
9.9 +produces a result tree consisting of instruction-oriented program nodes.
9.10 +
9.11 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
9.12 +
9.13 +This software is free software; you can redistribute it and/or
9.14 +modify it under the terms of the GNU General Public License as
9.15 +published by the Free Software Foundation; either version 2 of
9.16 +the License, or (at your option) any later version.
9.17 +
9.18 +This software is distributed in the hope that it will be useful,
9.19 +but WITHOUT ANY WARRANTY; without even the implied warranty of
9.20 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9.21 +GNU General Public License for more details.
9.22 +
9.23 +You should have received a copy of the GNU General Public
9.24 +License along with this library; see the file LICENCE.txt
9.25 +If not, write to the Free Software Foundation, Inc.,
9.26 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
9.27 +
9.28 +--------
9.29 +
9.30 +To use this module, the easiest approach is to use the simplify function:
9.31 +
9.32 +simplify(filename)
9.33 +
9.34 +The more complicated approach involves first instantiating a Simplifier object:
9.35 +
9.36 +simplifier = Simplifier()
9.37 +
9.38 +Then, applying the simplifier to an AST tree:
9.39 +
9.40 +module = compiler.parseFile(filename)
9.41 +simplifier.process(module)
9.42 +"""
9.43 +
9.44 +from simplify.simplified import *
9.45 +import compiler.ast
9.46 +import os
9.47 +
9.48 +class Simplifier(Visitor):
9.49 +
9.50 + """
9.51 + A simplifying visitor for AST nodes.
9.52 +
9.53 + Covered: Add, And, Assert, AssAttr, AssList, AssName, AssTuple, Assign,
9.54 + AugAssign, Bitand, Break, CallFunc, Class, Compare, Const,
9.55 + Continue, Dict, Discard, Div, FloorDiv, For, From, Function,
9.56 + Getattr, Global, If, Import, Invert, Keyword, Lambda, List,
9.57 + ListComp, ListCompFor, ListCompIf, Mod, Module, Mul, Name, Not, Or,
9.58 + Pass, Power, Print, Printnl, Raise, Return, Slice, Sliceobj, Stmt,
9.59 + Sub, Subscript, TryExcept, TryFinally, Tuple, While, UnaryAdd,
9.60 + UnarySub.
9.61 +
9.62 + Missing: Backquote, Bitor, Bitxor, Decorators, Ellipsis, Exec, LeftShift,
9.63 + RightShift, Yield.
9.64 + """
9.65 +
9.66 + def __init__(self, builtins=0):
9.67 +
9.68 + """
9.69 + Initialise the simplifier with the optional 'builtins' parameter
9.70 + indicating whether the module contains the built-in classes and
9.71 + functions.
9.72 + """
9.73 +
9.74 + Visitor.__init__(self)
9.75 + self.subprograms = [] # Subprograms outside the tree.
9.76 + self.structures = [] # Structures/classes.
9.77 + self.constants = {} # Constants.
9.78 + self.current_subprograms = [] # Current subprograms being processed.
9.79 + self.current_structures = [] # Current structures being processed.
9.80 + self.within_class = 0 # Whether a class is being defined.
9.81 + self.builtins = builtins # Whether the builtins are being processed.
9.82 +
9.83 + # Convenience attributes.
9.84 +
9.85 + self.subnames = {}
9.86 +
9.87 + # For compiler package mechanisms.
9.88 +
9.89 + self.visitor = self
9.90 +
9.91 + def process(self, node, name):
9.92 + result = self.dispatch(node, name)
9.93 + result.simplifier = self
9.94 + return result
9.95 +
9.96 + def dispatch_or_none(self, node, *args):
9.97 + if node is not None:
9.98 + return self.dispatch(node, *args)
9.99 + else:
9.100 + return LoadName(node, name="None")
9.101 +
9.102 + # Top-level transformation.
9.103 +
9.104 + def visitModule(self, module, name=None):
9.105 +
9.106 + """
9.107 + Process the given 'module', producing a Module object which contains the
9.108 + resulting program nodes. If the optional 'name' is provided, the 'name'
9.109 + attribute is set on the Module object using a value other than None.
9.110 + """
9.111 +
9.112 + result = self.module = Module(module, 1, name=name)
9.113 + result.code = self.dispatch(module.node)
9.114 + return result
9.115 +
9.116 + # Node transformations.
9.117 +
9.118 + def visitAdd(self, add):
9.119 + return self._visitBinary(add, "__add__", "__radd__")
9.120 +
9.121 + def visitAnd(self, and_):
9.122 +
9.123 + """
9.124 + Make a subprogram for the 'and_' node and record its contents inside the
9.125 + subprogram. Convert...
9.126 +
9.127 + And (test)
9.128 + (test)
9.129 + ...
9.130 +
9.131 + ...to:
9.132 +
9.133 + Subprogram -> Conditional (test) -> ReturnFromBlock ...
9.134 + (else) -> Conditional (test) -> ReturnFromBlock ...
9.135 + (else) -> ...
9.136 + """
9.137 +
9.138 + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
9.139 + self.current_subprograms.append(subprogram)
9.140 +
9.141 + # In the subprogram, make instructions which store each operand, test
9.142 + # for each operand's truth status, and if appropriate return from the
9.143 + # subprogram with the value of the operand.
9.144 +
9.145 + last = and_.nodes[-1]
9.146 + results = nodes = []
9.147 +
9.148 + for node in and_.nodes:
9.149 + expr = self.dispatch(node)
9.150 +
9.151 + # Return from the subprogram where the test is not satisfied.
9.152 +
9.153 + if node is not last:
9.154 + nodes += [
9.155 + StoreTemp(expr=expr),
9.156 + Conditional(
9.157 + test=self._visitNot(LoadTemp()),
9.158 + body=[
9.159 + ReturnFromBlock(
9.160 + expr=LoadTemp()
9.161 + )
9.162 + ],
9.163 + else_=[
9.164 + ReleaseTemp()
9.165 + # Subsequent operations go here!
9.166 + ]
9.167 + )
9.168 + ]
9.169 +
9.170 + # Put subsequent operations in the else section of this conditional.
9.171 +
9.172 + nodes = nodes[-1].else_
9.173 +
9.174 + # For the last operation, return the result.
9.175 +
9.176 + else:
9.177 + nodes.append(ReturnFromBlock(expr=expr))
9.178 +
9.179 + # Finish the subprogram definition.
9.180 +
9.181 + subprogram.code = results
9.182 +
9.183 + self.current_subprograms.pop()
9.184 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.185 +
9.186 + # Make an invocation of the subprogram.
9.187 +
9.188 + result = InvokeRef(and_, 1, produces_result=1, ref=subprogram)
9.189 + return result
9.190 +
9.191 + def visitAssert(self, assert_):
9.192 + if assert_.fail:
9.193 + fail_args = [self.dispatch(assert_.fail)]
9.194 + else:
9.195 + fail_args = []
9.196 +
9.197 + result = Conditional(assert_, 1,
9.198 + test=self.dispatch(assert_.test),
9.199 + body=[],
9.200 + else_=[
9.201 + Raise(assert_,
9.202 + expr=InvokeFunction(assert_,
9.203 + expr=LoadName(name="AssertionError"),
9.204 + args=fail_args,
9.205 + star=None,
9.206 + dstar=None
9.207 + )
9.208 + )
9.209 + ]
9.210 + )
9.211 +
9.212 + # Make nice annotations for the viewer.
9.213 +
9.214 + assert_._raises = result.else_[0].expr
9.215 + return result
9.216 +
9.217 + # Assignments.
9.218 +
9.219 + def visitAssAttr(self, assattr, in_sequence=0):
9.220 + expr = self._visitAssNameOrAttr(assattr, in_sequence)
9.221 + lvalue = self.dispatch(assattr.expr)
9.222 + result = StoreAttr(assattr, 1, name=assattr.attrname, lvalue=lvalue, expr=expr)
9.223 + return result
9.224 +
9.225 + def visitAssign(self, assign):
9.226 + result = Assign(assign, 1)
9.227 + store = StoreTemp(expr=self.dispatch(assign.expr))
9.228 + release = ReleaseTemp()
9.229 + result.code = [store] + self.dispatches(assign.nodes, 0) + [release]
9.230 + return result
9.231 +
9.232 + def visitAssList(self, asslist, in_sequence=0):
9.233 + if not in_sequence:
9.234 + expr = LoadTemp()
9.235 + else:
9.236 + expr = InvokeFunction(asslist, expr=LoadAttr(expr=LoadTemp(), name="next"))
9.237 + result = Assign(asslist, 1)
9.238 + store = StoreTemp(expr=InvokeFunction(asslist, expr=LoadAttr(name="__iter__", expr=expr)))
9.239 + release = ReleaseTemp()
9.240 + result.code = [store] + self.dispatches(asslist.nodes, 1) + [release]
9.241 + return result
9.242 +
9.243 + visitAssTuple = visitAssList
9.244 +
9.245 + def _visitAssNameOrAttr(self, node, in_sequence):
9.246 + if not in_sequence:
9.247 + return LoadTemp()
9.248 + else:
9.249 + return InvokeFunction(node, expr=LoadAttr(expr=LoadTemp(), name="next"))
9.250 +
9.251 + def visitAssName(self, assname, in_sequence=0):
9.252 + expr = self._visitAssNameOrAttr(assname, in_sequence)
9.253 + result = StoreName(assname, 1, name=assname.name, expr=expr)
9.254 + return result
9.255 +
9.256 + augassign_methods = {
9.257 + "+=" : "__iadd__", "-=" : "__isub__", "*=" : "__imul__", "/=" : "__idiv__",
9.258 + "%=" : "__imod__", "**=" : "__ipow__", "<<=" : "__ilshift__", ">>=" : "__irshift__",
9.259 + "&=" : "__iand__", "^=" : "__ixor__", "|=" : "__ior__"
9.260 + }
9.261 +
9.262 + def visitAugAssign(self, augassign):
9.263 +
9.264 + """
9.265 + Convert the augmented assignment...
9.266 +
9.267 + AugAssign (node) -> Name | Getattr | Slice | Subscript
9.268 + (op)
9.269 + (expr)
9.270 +
9.271 + ...to:
9.272 +
9.273 + Assign (code) -> StoreTemp (expr) -> InvokeFunction (expr) -> LoadAttr (expr) -> <name>
9.274 + (name) -> <op>
9.275 + StoreName (name) -> <name>
9.276 + (expr) -> LoadTemp
9.277 + ReleaseTemp
9.278 + """
9.279 +
9.280 + result = Assign(augassign, 1)
9.281 + expr = self.dispatch(augassign.expr)
9.282 +
9.283 + # Simple augmented assignment: name += expr
9.284 +
9.285 + if isinstance(augassign.node, compiler.ast.Name):
9.286 + result.code = [
9.287 + StoreTemp(
9.288 + expr=InvokeFunction( # referenced below
9.289 + augassign,
9.290 + args=[expr],
9.291 + star=None,
9.292 + dstar=None,
9.293 + expr=LoadAttr(
9.294 + expr=self.dispatch(augassign.node),
9.295 + name=self.augassign_methods[augassign.op]
9.296 + )
9.297 + )
9.298 + ),
9.299 + StoreName(
9.300 + expr=LoadTemp(),
9.301 + name=augassign.node.name),
9.302 + ReleaseTemp()
9.303 + ]
9.304 +
9.305 + # Make nice annotations for the viewer.
9.306 +
9.307 + augassign._op_call = result.code[0].expr
9.308 +
9.309 + # Complicated augmented assignment: lvalue.attr += expr
9.310 +
9.311 + elif isinstance(augassign.node, compiler.ast.Getattr):
9.312 +
9.313 + # <lvalue> -> setattr(<lvalue>, getattr(<lvalue>, "attr").__xxx__(expr))
9.314 +
9.315 + result.code = [
9.316 + StoreTemp(
9.317 + index="expr",
9.318 + expr=self.dispatch(augassign.node.expr)
9.319 + ),
9.320 + StoreTemp(
9.321 + expr=InvokeFunction( # referenced below
9.322 + augassign,
9.323 + args=[expr], star=None, dstar=None,
9.324 + expr=LoadAttr(
9.325 + expr=LoadAttr(augassign.node, 1,
9.326 + expr=LoadTemp(index="expr"),
9.327 + name=augassign.node.attrname
9.328 + ),
9.329 + name=self.augassign_methods[augassign.op]
9.330 + )
9.331 + )
9.332 + ),
9.333 + StoreAttr(
9.334 + expr=LoadTemp(),
9.335 + lvalue=LoadTemp(index="expr"),
9.336 + name=augassign.node.attrname
9.337 + ),
9.338 + ReleaseTemp(index="expr"),
9.339 + ReleaseTemp()
9.340 + ]
9.341 +
9.342 + # Make nice annotations for the viewer.
9.343 +
9.344 + augassign._op_call = result.code[1].expr
9.345 +
9.346 + # Complicated augassign using slices: lvalue[lower:upper] += expr
9.347 +
9.348 + elif isinstance(augassign.node, compiler.ast.Slice):
9.349 +
9.350 + # <lvalue>, <lower>, <upper> -> <lvalue>.__setslice__(<lower>, <upper>, <lvalue>.__getslice__(<lower>, <upper>).__xxx__(expr))
9.351 +
9.352 + result.code = [
9.353 + StoreTemp(
9.354 + index="expr",
9.355 + expr=self.dispatch(augassign.node.expr)
9.356 + ),
9.357 + StoreTemp(
9.358 + index="lower",
9.359 + expr=self.dispatch_or_none(augassign.node.lower)
9.360 + ),
9.361 + StoreTemp(
9.362 + index="upper",
9.363 + expr=self.dispatch_or_none(augassign.node.upper)
9.364 + ),
9.365 + StoreTemp(
9.366 + expr=InvokeFunction( # referenced below
9.367 + augassign,
9.368 + args=[expr], star=None, dstar=None,
9.369 + expr=LoadAttr(
9.370 + expr=self._visitSlice(
9.371 + augassign.node,
9.372 + LoadTemp(index="expr"),
9.373 + LoadTemp(index="lower"),
9.374 + LoadTemp(index="upper"),
9.375 + "OP_APPLY"),
9.376 + name=self.augassign_methods[augassign.op]
9.377 + )
9.378 + )
9.379 + ),
9.380 + self._visitSlice(
9.381 + augassign.node,
9.382 + LoadTemp(index="expr"),
9.383 + LoadTemp(index="lower"),
9.384 + LoadTemp(index="upper"),
9.385 + "OP_ASSIGN",
9.386 + LoadTemp()
9.387 + ),
9.388 + ReleaseTemp(index="expr"),
9.389 + ReleaseTemp(index="lower"),
9.390 + ReleaseTemp(index="upper"),
9.391 + ReleaseTemp()
9.392 + ]
9.393 +
9.394 + # Make nice annotations for the viewer.
9.395 +
9.396 + augassign._op_call = result.code[3].expr
9.397 +
9.398 + # Complicated augassign using subscripts: lvalue[subs] += expr
9.399 +
9.400 + elif isinstance(augassign.node, compiler.ast.Subscript):
9.401 +
9.402 + # <lvalue>, <subs> -> <lvalue>.__setitem__(<subs>, <lvalue>.__getitem__(<subs>).__xxx__(expr))
9.403 +
9.404 + result.code = [
9.405 + StoreTemp(index="expr", expr=self.dispatch(augassign.node.expr)),
9.406 + StoreTemp(index="subs", expr=self._visitSubscriptSubs(augassign.node, augassign.node.subs)),
9.407 + StoreTemp(
9.408 + expr=InvokeFunction( # referenced below
9.409 + augassign,
9.410 + args=[expr], star=None, dstar=None,
9.411 + expr=LoadAttr(
9.412 + expr=self._visitSubscript(
9.413 + augassign.node,
9.414 + LoadTemp(index="expr"),
9.415 + LoadTemp(index="subs"),
9.416 + "OP_APPLY"
9.417 + ),
9.418 + name=self.augassign_methods[augassign.op]
9.419 + )
9.420 + )
9.421 + ),
9.422 + self._visitSubscript(
9.423 + augassign.node,
9.424 + LoadTemp(index="expr"),
9.425 + LoadTemp(index="subs"),
9.426 + "OP_ASSIGN",
9.427 + LoadTemp()
9.428 + ),
9.429 + ReleaseTemp(index="expr"),
9.430 + ReleaseTemp(index="subs"),
9.431 + ReleaseTemp()
9.432 + ]
9.433 +
9.434 + # Make nice annotations for the viewer.
9.435 +
9.436 + augassign._op_call = result.code[2].expr
9.437 +
9.438 + else:
9.439 + raise NotImplementedError, augassign.node.__class__
9.440 +
9.441 + return result
9.442 +
9.443 + def visitBitand(self, bitand):
9.444 +
9.445 + """
9.446 + Make a subprogram for the 'bitand' node and record its contents inside the
9.447 + subprogram. Convert...
9.448 +
9.449 + Bitand (node)
9.450 + (node)
9.451 + ...
9.452 +
9.453 + ...to:
9.454 +
9.455 + Subprogram -> Conditional (test) -> ReturnFromBlock ...
9.456 + (else) -> Conditional (test) -> ReturnFromBlock ...
9.457 + (else) -> ...
9.458 + """
9.459 +
9.460 + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
9.461 + self.current_subprograms.append(subprogram)
9.462 +
9.463 + # In the subprogram, make instructions which store each operand, test
9.464 + # for each operand's truth status, and if appropriate return from the
9.465 + # subprogram with the value of the operand.
9.466 +
9.467 + last = bitand.nodes[-1]
9.468 + results = nodes = []
9.469 +
9.470 + # Start by storing the first operand.
9.471 +
9.472 + nodes += [
9.473 + StoreTemp(expr=self.dispatch(bitand.nodes[0]))
9.474 + ]
9.475 +
9.476 + # For viewing purposes, record invocations on the AST node.
9.477 +
9.478 + bitand._ops = []
9.479 +
9.480 + for node in bitand.nodes[1:]:
9.481 +
9.482 + # Make a new AST-style node to wrap the operation program nodes.
9.483 +
9.484 + new_op = Op("&", node)
9.485 + bitand._ops.append(new_op)
9.486 +
9.487 + # Generate the operation involving the previous result and the
9.488 + # current operand.
9.489 +
9.490 + expr = self._visitBinaryOp(new_op, LoadTemp(), self.dispatch(node), "__and__", "__rand__")
9.491 +
9.492 + # Return from the subprogram where the test is not satisfied.
9.493 +
9.494 + if node is not last:
9.495 + nodes += [
9.496 + StoreTemp(expr=expr),
9.497 + Conditional(
9.498 + test=self._visitNot(LoadTemp()),
9.499 + body=[
9.500 + ReturnFromBlock(
9.501 + expr=LoadTemp()
9.502 + )
9.503 + ],
9.504 + else_=[
9.505 + # Subsequent operations go here!
9.506 + ]
9.507 + )
9.508 + ]
9.509 +
9.510 + # Put subsequent operations in the else section of this conditional.
9.511 +
9.512 + nodes = nodes[-1].else_
9.513 +
9.514 + # For the last operation, return the result.
9.515 +
9.516 + else:
9.517 + nodes.append(ReturnFromBlock(expr=expr))
9.518 +
9.519 + # Finish the subprogram definition.
9.520 +
9.521 + subprogram.code = results
9.522 +
9.523 + self.current_subprograms.pop()
9.524 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.525 +
9.526 + # Make an invocation of the subprogram.
9.527 +
9.528 + result = InvokeRef(bitand, 1, produces_result=1, ref=subprogram)
9.529 + return result
9.530 +
9.531 + def visitBreak(self, break_):
9.532 + result = ReturnFromBlock(break_, 1)
9.533 + return result
9.534 +
9.535 + def visitCallFunc(self, callfunc):
9.536 + result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args))
9.537 + if callfunc.star_args is not None:
9.538 + result.star = self.dispatch(callfunc.star_args)
9.539 + if callfunc.dstar_args is not None:
9.540 + result.dstar = self.dispatch(callfunc.dstar_args)
9.541 + result.expr = self.dispatch(callfunc.node)
9.542 + return result
9.543 +
9.544 + def visitClass(self, class_):
9.545 +
9.546 + # Add "object" if the class is not "object" and has an empty bases list.
9.547 +
9.548 + if class_.name != "object" and not class_.bases:
9.549 + bases = [compiler.ast.Name("object")]
9.550 + else:
9.551 + bases = class_.bases
9.552 +
9.553 + structure = Class(name=class_.name, module=self.module, bases=self.dispatches(bases))
9.554 + self.structures.append(structure)
9.555 + within_class = self.within_class
9.556 + self.within_class = 1
9.557 +
9.558 + # Make a subprogram which initialises the class structure.
9.559 +
9.560 + subprogram = Subprogram(name=None, module=self.module, structure=structure, params=[], star=None, dstar=None)
9.561 + self.current_subprograms.append(subprogram)
9.562 + self.current_structures.append(structure) # mostly for name construction
9.563 +
9.564 + # The class is initialised using the code found inside.
9.565 +
9.566 + subprogram.code = self.dispatch(class_.code) + [ReturnFromBlock()]
9.567 +
9.568 + self.within_class = within_class
9.569 + self.current_structures.pop()
9.570 + self.current_subprograms.pop()
9.571 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.572 +
9.573 + # Make a definition of the class associating it with a name.
9.574 +
9.575 + result = Assign(
9.576 + code=[
9.577 + StoreName(class_, 1, # defines the class
9.578 + name=class_.name,
9.579 + expr=LoadRef(ref=structure)
9.580 + ),
9.581 + InvokeRef(
9.582 + class_,
9.583 + share_locals=0, # override the local sharing usually in InvokeRef
9.584 + ref=subprogram
9.585 + )
9.586 + ]
9.587 + )
9.588 + return result
9.589 +
9.590 + comparison_methods = {
9.591 + "==" : ("__eq__", "__ne__"),
9.592 + "!=" : ("__ne__", "__eq__"),
9.593 + "<" : ("__lt__", "__gt__"),
9.594 + "<=" : ("__le__", "__ge__"),
9.595 + ">=" : ("__ge__", "__le__"),
9.596 + ">" : ("__gt__", "__lt__"),
9.597 + "is" : None,
9.598 + "is not" : None,
9.599 + "in" : None,
9.600 + "not in" : None
9.601 + }
9.602 +
9.603 + def visitCompare(self, compare):
9.604 +
9.605 + """
9.606 + Make a subprogram for the 'compare' node and record its contents inside
9.607 + the subprogram. Convert...
9.608 +
9.609 + Compare (expr)
9.610 + (name/node)
9.611 + ...
9.612 +
9.613 + ...to:
9.614 +
9.615 + InvokeRef -> Subprogram -> Conditional (test) -> (body)
9.616 + (else) -> Conditional (test) -> (body)
9.617 + (else) -> ...
9.618 + """
9.619 +
9.620 + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
9.621 + self.current_subprograms.append(subprogram)
9.622 +
9.623 + # In the subprogram, make instructions which invoke a method on the
9.624 + # first operand of each operand pair and, if appropriate, return with
9.625 + # the value from that method.
9.626 +
9.627 + last = compare.ops[-1]
9.628 + previous = self.dispatch(compare.expr)
9.629 + results = nodes = []
9.630 +
9.631 + # For viewing purposes, record invocations on the AST node.
9.632 +
9.633 + compare._ops = []
9.634 +
9.635 + for op in compare.ops:
9.636 + op_name, node = op
9.637 +
9.638 + # Make a new AST-style node to wrap the operation program nodes.
9.639 +
9.640 + new_op = Op(op_name, node)
9.641 + compare._ops.append(new_op)
9.642 +
9.643 + expr = self.dispatch(node)
9.644 +
9.645 + # Identify the operation and produce the appropriate method call.
9.646 +
9.647 + method_names = self.comparison_methods[op_name]
9.648 + if method_names:
9.649 + first_name, alternative_name = method_names
9.650 + invocation = self._visitBinaryCompareOp(new_op, previous, expr, first_name, alternative_name)
9.651 +
9.652 + elif op_name == "is":
9.653 + invocation = InvokeFunction(
9.654 + new_op, 1,
9.655 + expr=LoadName(name="__is__"),
9.656 + args=[previous, expr],
9.657 + star=None,
9.658 + dstar=None)
9.659 +
9.660 + elif op_name == "is not":
9.661 + invocation = Not(
9.662 + new_op, 1,
9.663 + expr=InvokeFunction(
9.664 + new_op,
9.665 + expr=LoadName(name="__is__"),
9.666 + args=[previous, expr],
9.667 + star=None,
9.668 + dstar=None)
9.669 + )
9.670 +
9.671 + elif op_name == "in":
9.672 + invocation = InvokeFunction(
9.673 + new_op, 1,
9.674 + expr=LoadAttr(
9.675 + expr=previous,
9.676 + name="__contains__"
9.677 + ),
9.678 + args=[expr],
9.679 + star=None,
9.680 + dstar=None)
9.681 +
9.682 + elif op_name == "not in":
9.683 + invocation = Not(
9.684 + new_op, 1,
9.685 + expr=InvokeFunction(
9.686 + new_op,
9.687 + expr=LoadAttr(
9.688 + expr=previous,
9.689 + name="__contains__"
9.690 + ),
9.691 + args=[expr],
9.692 + star=None,
9.693 + dstar=None)
9.694 + )
9.695 +
9.696 + else:
9.697 + raise NotImplementedError, op_name
9.698 +
9.699 + nodes.append(StoreTemp(expr=invocation))
9.700 +
9.701 + # Return from the subprogram where the test is not satisfied.
9.702 +
9.703 + if op is not last:
9.704 + nodes.append(
9.705 + Conditional(
9.706 + test=self._visitNot(LoadTemp()),
9.707 + body=[
9.708 + ReturnFromBlock(expr=LoadTemp())
9.709 + ],
9.710 + else_=[
9.711 + ReleaseTemp()
9.712 + # Subsequent operations go here!
9.713 + ]
9.714 + )
9.715 + )
9.716 +
9.717 + # Put subsequent operations in the else section of this conditional.
9.718 +
9.719 + nodes = nodes[-1].else_
9.720 +
9.721 + # For the last operation, return the result.
9.722 +
9.723 + else:
9.724 + nodes.append(
9.725 + ReturnFromBlock(expr=LoadTemp(release=1))
9.726 + )
9.727 +
9.728 + previous = expr
9.729 +
9.730 + # Finish the subprogram definition.
9.731 +
9.732 + subprogram.code = results
9.733 +
9.734 + self.current_subprograms.pop()
9.735 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.736 +
9.737 + # Make an invocation of the subprogram.
9.738 +
9.739 + result = InvokeRef(compare, 1, produces_result=1, ref=subprogram)
9.740 + return result
9.741 +
9.742 + def visitConst(self, const):
9.743 + key = "%s-%s" % (const.value.__class__.__name__, const.value)
9.744 + if not self.constants.has_key(key):
9.745 + self.constants[key] = Constant(name=repr(const.value), value=const.value)
9.746 + result = InvokeFunction(const, 1, expr=LoadName(name=self.constants[key].typename))
9.747 + return result
9.748 +
9.749 + def visitContinue(self, continue_):
9.750 + result = InvokeRef(continue_, 1, ref=self.current_subprograms[-1])
9.751 + return result
9.752 +
9.753 + def visitDict(self, dict):
9.754 + result = InvokeFunction(dict, 1, expr=LoadName(name="dict"))
9.755 + args = []
9.756 + for key, value in dict.items:
9.757 + tuple = InvokeFunction(dict, expr=LoadName(name="tuple"))
9.758 + tuple.set_args([self.dispatch(key), self.dispatch(value)])
9.759 + args.append(tuple)
9.760 + result.set_args(args)
9.761 + return result
9.762 +
9.763 + def visitDiscard(self, discard):
9.764 + return self.dispatch(discard.expr)
9.765 +
9.766 + def visitDiv(self, div):
9.767 + return self._visitBinary(div, "__div__", "__rdiv__")
9.768 +
9.769 + def visitFloorDiv(self, floordiv):
9.770 + return self._visitBinary(floordiv, "__floordiv__", "__rfloordiv__")
9.771 +
9.772 + def visitFor(self, for_):
9.773 +
9.774 + """
9.775 + Make a subprogram for the 'for_' node and record its contents inside the
9.776 + subprogram. Convert...
9.777 +
9.778 + For (assign)
9.779 + (body)
9.780 + (else)
9.781 +
9.782 + ...to:
9.783 +
9.784 + Assign (assign #1)
9.785 + Invoke -> Subprogram -> Try (body) -> (assign #2)
9.786 + (body)
9.787 + Invoke subprogram
9.788 + (handler) -> ...
9.789 + (else) -> ...
9.790 + """
9.791 +
9.792 + return self._visitFor(for_, self.dispatches(for_.body), for_.else_)
9.793 +
9.794 + def _visitFor(self, node, body_stmt, else_=None):
9.795 +
9.796 + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[])
9.797 + self.current_subprograms.append(subprogram)
9.798 +
9.799 + # Always return from conditional sections/subprograms.
9.800 +
9.801 + if else_ is not None:
9.802 + else_stmt = self.dispatch(else_) + [ReturnFromBlock()]
9.803 + else:
9.804 + else_stmt = [ReturnFromBlock()]
9.805 +
9.806 + # Wrap the assignment in a try...except statement.
9.807 + # Inside the body, add a recursive invocation to the subprogram.
9.808 +
9.809 + subprogram.code = [
9.810 + Try(
9.811 + body=[
9.812 + Assign(
9.813 + code=[
9.814 + StoreTemp(
9.815 + expr=InvokeFunction(node,
9.816 + expr=LoadAttr(
9.817 + expr=LoadTemp(),
9.818 + name="next"
9.819 + )
9.820 + )
9.821 + ),
9.822 + self.dispatch(node.assign),
9.823 + ReleaseTemp()
9.824 + ])
9.825 + ] + body_stmt + [
9.826 + InvokeRef(
9.827 + node,
9.828 + ref=subprogram
9.829 + )
9.830 + ],
9.831 + handler=[
9.832 + Conditional(
9.833 + test=InvokeFunction(
9.834 + node,
9.835 + expr=LoadName(name="isinstance"),
9.836 + args=[LoadExc(), LoadName(name="StopIteration")],
9.837 + star=None,
9.838 + dstar=None),
9.839 + body=else_stmt,
9.840 + else_=[
9.841 + Raise(
9.842 + expr=LoadExc()
9.843 + )
9.844 + ]
9.845 + )
9.846 + ],
9.847 + else_=[],
9.848 + finally_=[]
9.849 + ),
9.850 + ReturnFromBlock()
9.851 + ]
9.852 +
9.853 + # Finish the subprogram definition.
9.854 +
9.855 + self.current_subprograms.pop()
9.856 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.857 +
9.858 + # Obtain an iterator for the sequence involved.
9.859 + # Then, make an invocation of the subprogram.
9.860 +
9.861 + result = Assign(node, 1,
9.862 + code=[
9.863 + StoreTemp(
9.864 + expr=InvokeFunction(
9.865 + node,
9.866 + expr=LoadAttr(
9.867 + name="__iter__",
9.868 + expr=self.dispatch(node.list)
9.869 + )
9.870 + )
9.871 + ),
9.872 + InvokeRef(node, ref=subprogram),
9.873 + ReleaseTemp()
9.874 + ]
9.875 + )
9.876 +
9.877 + # Make nice annotations for the viewer.
9.878 +
9.879 + node._iter_call = result.code[0].expr
9.880 + node._next_call = subprogram.code[0].body[0].code[0].expr
9.881 +
9.882 + return result
9.883 +
9.884 + def visitFrom(self, from_):
9.885 + result = Assign(from_, 1)
9.886 + code = []
9.887 + _names = []
9.888 + code.append(
9.889 + StoreTemp(
9.890 + expr=Import(name=from_.modname, alias=1)
9.891 + )
9.892 + )
9.893 + from_._modname = code[-1].expr
9.894 + for name, alias in from_.names:
9.895 + code.append(
9.896 + StoreName(
9.897 + expr=LoadAttr(
9.898 + expr=LoadTemp(),
9.899 + name=name),
9.900 + name=(alias or name)
9.901 + )
9.902 + )
9.903 + _names.append(code[-1].expr)
9.904 + code.append(ReleaseTemp())
9.905 + result.code = code
9.906 + from_._names = _names
9.907 + return result
9.908 +
9.909 + def _visitFunction(self, function, subprogram):
9.910 +
9.911 + """
9.912 + A common function generator which transforms the given 'function' node
9.913 + and initialises the given 'subprogram' appropriately.
9.914 + """
9.915 +
9.916 + # Discover star and dstar parameters.
9.917 +
9.918 + if function.flags & 4 != 0:
9.919 + has_star = 1
9.920 + else:
9.921 + has_star = 0
9.922 + if function.flags & 8 != 0:
9.923 + has_dstar = 1
9.924 + else:
9.925 + has_dstar = 0
9.926 +
9.927 + # Discover the number of defaults and positional parameters.
9.928 +
9.929 + ndefaults = len(function.defaults)
9.930 + npositional = len(function.argnames) - has_star - has_dstar
9.931 +
9.932 + # Produce star and dstar parameters with appropriate defaults.
9.933 +
9.934 + if has_star:
9.935 + star = (
9.936 + function.argnames[npositional],
9.937 + self.dispatch(compiler.ast.List([]))
9.938 + )
9.939 + else:
9.940 + star = None
9.941 + if has_dstar:
9.942 + dstar = (
9.943 + function.argnames[npositional + has_star],
9.944 + self.dispatch(compiler.ast.Dict([]))
9.945 + )
9.946 + else:
9.947 + dstar = None
9.948 +
9.949 + params = []
9.950 + for i in range(0, npositional - ndefaults):
9.951 + params.append((function.argnames[i], None))
9.952 +
9.953 + # Process defaults.
9.954 +
9.955 + for i in range(0, ndefaults):
9.956 + default = function.defaults[i]
9.957 + if default is not None:
9.958 + params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default)))
9.959 + else:
9.960 + params.append((function.argnames[npositional - ndefaults + i], None))
9.961 +
9.962 + subprogram.params = params
9.963 + subprogram.star = star
9.964 + subprogram.dstar = dstar
9.965 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.966 +
9.967 + def visitFunction(self, function):
9.968 +
9.969 + """
9.970 + Make a subprogram for the 'function' and record it outside the main
9.971 + tree. Produce something like the following:
9.972 +
9.973 + StoreName (name)
9.974 + (expr) -> LoadRef (ref) -> Subprogram (params)
9.975 + (star)
9.976 + (dstar)
9.977 + """
9.978 +
9.979 + subprogram = Subprogram(function, name=function.name, module=self.module, structures=self.current_structures[:],
9.980 + internal=0, returns_value=1, star=None, dstar=None, is_method=self.within_class, original_def=function)
9.981 +
9.982 + # Make nice annotations for the viewer.
9.983 +
9.984 + function._subprogram = subprogram
9.985 +
9.986 + self.current_subprograms.append(subprogram)
9.987 + within_class = self.within_class
9.988 + self.within_class = 0
9.989 +
9.990 + subprogram.code = self.dispatch(function.code) + [ReturnFromFunction()]
9.991 +
9.992 + self.within_class = within_class
9.993 + self.current_subprograms.pop()
9.994 + self._visitFunction(function, subprogram)
9.995 +
9.996 + # Make a definition of the function associating it with a name.
9.997 +
9.998 + result = StoreName(function, 1, name=function.name, expr=LoadRef(ref=subprogram))
9.999 + return result
9.1000 +
9.1001 + def visitGetattr(self, getattr):
9.1002 + result = LoadAttr(getattr, 1,
9.1003 + name=getattr.attrname,
9.1004 + expr=self.dispatch(getattr.expr)
9.1005 + )
9.1006 + return result
9.1007 +
9.1008 + def visitGlobal(self, global_):
9.1009 + result = Global(global_, 1,
9.1010 + names=global_.names
9.1011 + )
9.1012 + return result
9.1013 +
9.1014 + def visitIf(self, if_):
9.1015 +
9.1016 + """
9.1017 + Make conditionals for each test from an 'if_' AST node, adding the body
9.1018 + and putting each subsequent test as part of the conditional's else
9.1019 + section.
9.1020 +
9.1021 + Convert...
9.1022 +
9.1023 + If (test/body)
9.1024 + (test/body)
9.1025 + ...
9.1026 + (else/body)
9.1027 +
9.1028 + ...to:
9.1029 +
9.1030 + Conditional (test) -> (body)
9.1031 + (else) -> Conditional (test) -> (body)
9.1032 + (else) -> ...
9.1033 + """
9.1034 +
9.1035 +
9.1036 + results = nodes = []
9.1037 +
9.1038 + # Produce something like...
9.1039 + # expr.__bool__() ? body
9.1040 +
9.1041 + first = 1
9.1042 + for compare, stmt in if_.tests:
9.1043 +
9.1044 + # Set the first as the defining node.
9.1045 +
9.1046 + test = Conditional(if_, first,
9.1047 + test=InvokeFunction(
9.1048 + if_,
9.1049 + expr=LoadAttr(
9.1050 + expr=self.dispatch(compare),
9.1051 + name="__bool__"
9.1052 + ),
9.1053 + )
9.1054 + )
9.1055 + test.body = self.dispatch(stmt)
9.1056 + nodes.append(test)
9.1057 + nodes = test.else_ = []
9.1058 + first = 0
9.1059 +
9.1060 + # Add the compound statement from any else clause to the end.
9.1061 +
9.1062 + if if_.else_ is not None:
9.1063 + nodes += self.dispatch(if_.else_)
9.1064 +
9.1065 + result = results[0]
9.1066 + return result
9.1067 +
9.1068 + def visitImport(self, import_):
9.1069 + result = Assign(import_, 1)
9.1070 + code = []
9.1071 + _names = []
9.1072 + for path, alias in import_.names:
9.1073 + importer = Import(name=path, alias=alias)
9.1074 + top = alias or path.split(".")[0]
9.1075 + code.append(StoreName(expr=importer, name=top))
9.1076 + _names.append(code[-1].expr)
9.1077 + result.code = code
9.1078 + import_._names = _names
9.1079 + return result
9.1080 +
9.1081 + def visitInvert(self, invert):
9.1082 + return self._visitUnary(invert, "__invert__")
9.1083 +
9.1084 + def visitKeyword(self, keyword):
9.1085 + result = Keyword(keyword, 1,
9.1086 + name=keyword.name,
9.1087 + expr=self.dispatch(keyword.expr)
9.1088 + )
9.1089 + return result
9.1090 +
9.1091 + def visitLambda(self, lambda_):
9.1092 +
9.1093 + # Make a subprogram for the function and record it outside the main
9.1094 + # tree.
9.1095 +
9.1096 + subprogram = Subprogram(lambda_, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=lambda_)
9.1097 +
9.1098 + # Make nice annotations for the viewer.
9.1099 +
9.1100 + lambda_._subprogram = subprogram
9.1101 +
9.1102 + # Process the lambda contents.
9.1103 +
9.1104 + self.current_subprograms.append(subprogram)
9.1105 + subprogram.code = [ReturnFromFunction(expr=self.dispatch(lambda_.code))]
9.1106 + self.current_subprograms.pop()
9.1107 + self._visitFunction(lambda_, subprogram)
9.1108 +
9.1109 + # Get the subprogram reference to the lambda.
9.1110 +
9.1111 + return LoadRef(lambda_, 1, ref=subprogram)
9.1112 +
9.1113 + def visitList(self, list):
9.1114 +
9.1115 + # Make a subprogram for the list construction and record it outside the
9.1116 + # main tree.
9.1117 +
9.1118 + subprogram = Subprogram(list, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=list)
9.1119 + self.current_subprograms.append(subprogram)
9.1120 +
9.1121 + # Make nice annotations for the viewer.
9.1122 +
9.1123 + list._subprogram = subprogram
9.1124 +
9.1125 + subprogram.code=[
9.1126 + StoreTemp(
9.1127 + expr=InvokeFunction(
9.1128 + list,
9.1129 + expr=LoadName(
9.1130 + name="list"
9.1131 + ),
9.1132 + args=[],
9.1133 + star=None,
9.1134 + dstar=None
9.1135 + )
9.1136 + )
9.1137 + ]
9.1138 +
9.1139 + for node in list.nodes:
9.1140 + subprogram.code.append(
9.1141 + InvokeFunction(
9.1142 + list,
9.1143 + expr=LoadAttr(
9.1144 + expr=LoadTemp(),
9.1145 + name="append"
9.1146 + ),
9.1147 + args=[self.dispatch(node)],
9.1148 + star=None,
9.1149 + dstar=None
9.1150 + )
9.1151 + )
9.1152 +
9.1153 + subprogram.code.append(
9.1154 + ReturnFromBlock(
9.1155 + expr=LoadTemp(release=1)
9.1156 + )
9.1157 + )
9.1158 +
9.1159 + self.current_subprograms.pop()
9.1160 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.1161 +
9.1162 + # Make an invocation of the subprogram.
9.1163 +
9.1164 + result = InvokeRef(list, 1,
9.1165 + produces_result=1,
9.1166 + ref=subprogram
9.1167 + )
9.1168 + return result
9.1169 +
9.1170 + def visitListComp(self, listcomp):
9.1171 +
9.1172 + # Make a subprogram for the list comprehension and record it outside the
9.1173 + # main tree.
9.1174 +
9.1175 + subprogram = Subprogram(listcomp, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=listcomp)
9.1176 + self.current_subprograms.append(subprogram)
9.1177 +
9.1178 + # Make nice annotations for the viewer.
9.1179 +
9.1180 + listcomp._subprogram = subprogram
9.1181 +
9.1182 + # Add a temporary variable.
9.1183 + # Produce for loops within the subprogram.
9.1184 + # Return the result.
9.1185 +
9.1186 + subprogram.code = [
9.1187 + StoreTemp(
9.1188 + index="listcomp",
9.1189 + expr=InvokeFunction(
9.1190 + expr=LoadName(name="list"),
9.1191 + args=[],
9.1192 + star=None,
9.1193 + dstar=None
9.1194 + )
9.1195 + )
9.1196 + ] + self._visitListCompFor(listcomp, listcomp.quals) + [
9.1197 + ReturnFromBlock(
9.1198 + expr=LoadTemp(
9.1199 + index="listcomp",
9.1200 + release=1
9.1201 + )
9.1202 + )
9.1203 + ]
9.1204 +
9.1205 + self.current_subprograms.pop()
9.1206 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.1207 +
9.1208 + # Make an invocation of the subprogram.
9.1209 +
9.1210 + result = InvokeRef(listcomp, 1,
9.1211 + produces_result=1,
9.1212 + ref=subprogram
9.1213 + )
9.1214 + return result
9.1215 +
9.1216 + def _visitListCompFor(self, node, quals):
9.1217 + qual = quals[0]
9.1218 + if len(quals) > 1:
9.1219 + body = self._visitListCompFor(node, quals[1:])
9.1220 + if qual.ifs:
9.1221 + body = self._visitListCompIf(node, qual.ifs, body)
9.1222 + elif qual.ifs:
9.1223 + body = self._visitListCompIf(node, qual.ifs)
9.1224 + else:
9.1225 + body = self._visitListCompBody(node)
9.1226 + return [self._visitFor(qual, body)]
9.1227 +
9.1228 + def _visitListCompIf(self, node, ifs, expr=None):
9.1229 + if_ = ifs[0]
9.1230 + if len(ifs) > 1:
9.1231 + body = self._visitListCompIf(node, ifs[1:], expr)
9.1232 + elif expr is None:
9.1233 + body = self._visitListCompBody(node)
9.1234 + else:
9.1235 + body = expr
9.1236 + return [
9.1237 + Conditional(if_, 1,
9.1238 + test=InvokeFunction(
9.1239 + if_,
9.1240 + expr=LoadAttr(
9.1241 + expr=self.dispatch(if_.test),
9.1242 + name="__bool__"
9.1243 + ),
9.1244 + ),
9.1245 + body=body,
9.1246 + else_=[]
9.1247 + )
9.1248 + ]
9.1249 +
9.1250 + def _visitListCompBody(self, node):
9.1251 + return [
9.1252 + InvokeFunction(
9.1253 + expr=LoadAttr(
9.1254 + expr=LoadTemp(index="listcomp"),
9.1255 + name="append"
9.1256 + ),
9.1257 + args=[self.dispatch(node.expr)],
9.1258 + star=None,
9.1259 + dstar=None
9.1260 + )
9.1261 + ]
9.1262 +
9.1263 + def visitMod(self, mod):
9.1264 + return self._visitBinary(mod, "__mod__", "__rmod__")
9.1265 +
9.1266 + def visitMul(self, mul):
9.1267 + return self._visitBinary(mul, "__mul__", "__rmul__")
9.1268 +
9.1269 + def visitName(self, name):
9.1270 + result = LoadName(name, 1, name=name.name)
9.1271 + return result
9.1272 +
9.1273 + def _visitNot(self, expr, not_=None):
9.1274 + invocation = InvokeFunction(
9.1275 + not_, # NOTE: May need a real original node.
9.1276 + expr=LoadAttr(
9.1277 + expr=expr,
9.1278 + name="__bool__"
9.1279 + ),
9.1280 + )
9.1281 + if not_ is not None:
9.1282 + result = Not(not_, 1, expr=invocation)
9.1283 + else:
9.1284 + result = Not(expr=invocation)
9.1285 + return result
9.1286 +
9.1287 + def visitNot(self, not_):
9.1288 + return self._visitNot(self.dispatch(not_.expr), not_)
9.1289 +
9.1290 + def visitOr(self, or_):
9.1291 +
9.1292 + """
9.1293 + Make a subprogram for the 'or_' node and record its contents inside the
9.1294 + subprogram. Convert...
9.1295 +
9.1296 + Or (test)
9.1297 + (test)
9.1298 + ...
9.1299 +
9.1300 + ...to:
9.1301 +
9.1302 + Subprogram -> Conditional (test) -> ReturnFromBlock ...
9.1303 + (else) -> Conditional (test) -> ReturnFromBlock ...
9.1304 + (else) -> ...
9.1305 + """
9.1306 +
9.1307 + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
9.1308 + self.current_subprograms.append(subprogram)
9.1309 +
9.1310 + # In the subprogram, make instructions which store each operand, test
9.1311 + # for each operand's truth status, and if appropriate return from the
9.1312 + # subprogram with the value of the operand.
9.1313 +
9.1314 + last = or_.nodes[-1]
9.1315 + results = nodes = []
9.1316 +
9.1317 + for node in or_.nodes:
9.1318 + expr = self.dispatch(node)
9.1319 +
9.1320 + # Return from the subprogram where the test is satisfied.
9.1321 +
9.1322 + if node is not last:
9.1323 + nodes.append(StoreTemp(expr=expr))
9.1324 + invocation = InvokeFunction(or_, expr=LoadAttr(expr=LoadTemp(), name="__bool__"))
9.1325 + test = Conditional(test=invocation, body=[ReturnFromBlock(expr=LoadTemp())])
9.1326 + nodes.append(test)
9.1327 +
9.1328 + # Put subsequent operations in the else section of this conditional.
9.1329 +
9.1330 + nodes = test.else_ = [ReleaseTemp()]
9.1331 +
9.1332 + # For the last operation, return the result.
9.1333 +
9.1334 + else:
9.1335 + nodes.append(
9.1336 + ReturnFromBlock(expr=expr)
9.1337 + )
9.1338 +
9.1339 + # Finish the subprogram definition.
9.1340 +
9.1341 + subprogram.code = results
9.1342 +
9.1343 + self.current_subprograms.pop()
9.1344 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.1345 +
9.1346 + # Make an invocation of the subprogram.
9.1347 +
9.1348 + result = InvokeRef(or_, 1,
9.1349 + produces_result=1,
9.1350 + ref=subprogram
9.1351 + )
9.1352 + return result
9.1353 +
9.1354 + def visitPass(self, pass_):
9.1355 + return Pass(pass_, 1)
9.1356 +
9.1357 + def visitPower(self, power):
9.1358 + return self._visitBinary(power, "__pow__", "__rpow__")
9.1359 +
9.1360 + def visitPrint(self, print_):
9.1361 +
9.1362 + """
9.1363 + Convert...
9.1364 +
9.1365 + Print (dest) ->
9.1366 + (nodes)
9.1367 +
9.1368 + ...to:
9.1369 +
9.1370 + StoreTemp (index) -> "print"
9.1371 + (expr) -> LoadAttr (expr) -> (dest)
9.1372 + (name) -> "write"
9.1373 + InvokeFunction (expr) -> LoadTemp (index) -> "print"
9.1374 + (args) -> [(node)]
9.1375 + ReleaseTemp (index) -> "print"
9.1376 + """
9.1377 +
9.1378 + if print_.dest is not None:
9.1379 + dest = self.dispatch(print_.dest)
9.1380 + else:
9.1381 + dest = self.dispatch(compiler.ast.Name("stdout"))
9.1382 +
9.1383 + result = Assign(print_, 1,
9.1384 + code=[
9.1385 + StoreTemp(
9.1386 + index="print",
9.1387 + expr=LoadAttr(
9.1388 + expr=dest,
9.1389 + name="write"
9.1390 + )
9.1391 + )
9.1392 + ]
9.1393 + )
9.1394 +
9.1395 + for node in print_.nodes:
9.1396 + result.code.append(
9.1397 + InvokeFunction(
9.1398 + print_,
9.1399 + expr=LoadTemp(index="print"),
9.1400 + args=[self.dispatch(node)],
9.1401 + star=None,
9.1402 + dstar=None
9.1403 + )
9.1404 + )
9.1405 +
9.1406 + result.code.append(
9.1407 + ReleaseTemp(index="print")
9.1408 + )
9.1409 +
9.1410 + return result
9.1411 +
9.1412 + def visitPrintnl(self, printnl):
9.1413 + result = self.visitPrint(printnl)
9.1414 + result.code.insert(
9.1415 + len(result.code) - 1,
9.1416 + InvokeFunction(
9.1417 + printnl,
9.1418 + expr=LoadTemp(index="print"),
9.1419 + args=[self.dispatch(compiler.ast.Const("\n"))],
9.1420 + star=None,
9.1421 + dstar=None
9.1422 + )
9.1423 + )
9.1424 + return result
9.1425 +
9.1426 + def visitRaise(self, raise_):
9.1427 + result = Raise(raise_, 1)
9.1428 + if raise_.expr2 is None:
9.1429 + result.expr = self.dispatch(raise_.expr1)
9.1430 + else:
9.1431 + result.expr = InvokeFunction(
9.1432 + raise_,
9.1433 + expr=self.dispatch(raise_.expr1),
9.1434 + args=[self.dispatch(raise_.expr2)],
9.1435 + star=None,
9.1436 + dstar=None
9.1437 + )
9.1438 + if raise_.expr3 is not None:
9.1439 + result.traceback = self.dispatch(raise_.expr3)
9.1440 + else:
9.1441 + result.traceback = None
9.1442 + return result
9.1443 +
9.1444 + def visitReturn(self, return_):
9.1445 + result = ReturnFromFunction(return_, 1,
9.1446 + expr=self.dispatch(return_.value)
9.1447 + )
9.1448 + return result
9.1449 +
9.1450 + def _visitSlice(self, slice, expr, lower, upper, flags, value=None):
9.1451 + if flags == "OP_ASSIGN":
9.1452 + result = InvokeFunction(slice, 1,
9.1453 + expr=LoadAttr(
9.1454 + expr=expr,
9.1455 + name="__setslice__"
9.1456 + ),
9.1457 + star=None,
9.1458 + dstar=None,
9.1459 + args=[lower, upper, value]
9.1460 + )
9.1461 + elif flags == "OP_APPLY":
9.1462 + args = []
9.1463 + result = InvokeFunction(slice, 1,
9.1464 + expr=LoadAttr(
9.1465 + expr=expr,
9.1466 + name="__getslice__"
9.1467 + ),
9.1468 + star=None,
9.1469 + dstar=None,
9.1470 + args=[lower, upper]
9.1471 + )
9.1472 + elif flags == "OP_DELETE":
9.1473 + args = []
9.1474 + result = InvokeFunction(slice, 1,
9.1475 + expr=LoadAttr(
9.1476 + expr=expr,
9.1477 + name="__delslice__"
9.1478 + ),
9.1479 + star=None,
9.1480 + dstar=None,
9.1481 + args=[lower, upper]
9.1482 + )
9.1483 + else:
9.1484 + raise NotImplementedError, flags
9.1485 +
9.1486 + return result
9.1487 +
9.1488 + def visitSlice(self, slice, in_sequence=0):
9.1489 + return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower),
9.1490 + self.dispatch_or_none(slice.upper), slice.flags, self._visitAssNameOrAttr(slice, in_sequence))
9.1491 +
9.1492 + def visitSliceobj(self, sliceobj):
9.1493 + return InvokeFunction(sliceobj, 1,
9.1494 + expr=LoadName(name="slice"),
9.1495 + args=self.dispatches(sliceobj.nodes),
9.1496 + star=None,
9.1497 + dstar=None
9.1498 + )
9.1499 +
9.1500 + def visitStmt(self, stmt):
9.1501 + return self.dispatches(stmt.nodes)
9.1502 +
9.1503 + def visitSub(self, sub):
9.1504 + return self._visitBinary(sub, "__sub__", "__rsub__")
9.1505 +
9.1506 + def _visitSubscript(self, subscript, expr, subs, flags, value=None):
9.1507 + if flags == "OP_ASSIGN":
9.1508 + result = InvokeFunction(subscript, 1,
9.1509 + expr=LoadAttr(
9.1510 + expr=expr,
9.1511 + name="__setitem__"
9.1512 + ),
9.1513 + star=None,
9.1514 + dstar=None,
9.1515 + args=[subs, value]
9.1516 + )
9.1517 + elif flags == "OP_APPLY":
9.1518 + args = []
9.1519 + result = InvokeFunction(subscript, 1,
9.1520 + expr=LoadAttr(
9.1521 + expr=expr,
9.1522 + name="__getitem__"
9.1523 + ),
9.1524 + star=None,
9.1525 + dstar=None,
9.1526 + args=[subs]
9.1527 + )
9.1528 + elif flags == "OP_DELETE":
9.1529 + args = []
9.1530 + result = InvokeFunction(subscript, 1,
9.1531 + expr=LoadAttr(
9.1532 + expr=expr,
9.1533 + name="__delitem__"
9.1534 + ),
9.1535 + star=None,
9.1536 + dstar=None,
9.1537 + args=[subs]
9.1538 + )
9.1539 + else:
9.1540 + raise NotImplementedError, flags
9.1541 +
9.1542 + return result
9.1543 +
9.1544 + def _visitSubscriptSubs(self, node, subs):
9.1545 + if len(subs) == 1:
9.1546 + return self.dispatch(subs[0])
9.1547 + else:
9.1548 + return InvokeFunction(node, 1,
9.1549 + expr=LoadName(name="tuple"),
9.1550 + args=self.dispatches(subs),
9.1551 + star=None,
9.1552 + dstar=None
9.1553 + )
9.1554 +
9.1555 + def visitSubscript(self, subscript, in_sequence=0):
9.1556 + return self._visitSubscript(
9.1557 + subscript, self.dispatch(subscript.expr), self._visitSubscriptSubs(subscript, subscript.subs), subscript.flags,
9.1558 + self._visitAssNameOrAttr(subscript, in_sequence)
9.1559 + )
9.1560 +
9.1561 + def visitTryExcept(self, tryexcept):
9.1562 +
9.1563 + """
9.1564 + Make conditionals for each handler associated with a 'tryexcept' node.
9.1565 +
9.1566 + Convert...
9.1567 +
9.1568 + TryExcept (body)
9.1569 + (else)
9.1570 + (spec/assign/stmt)
9.1571 + ...
9.1572 +
9.1573 + ...to:
9.1574 +
9.1575 + Try (body)
9.1576 + (else)
9.1577 + (handler) -> Conditional (test) -> (stmt)
9.1578 + (body) -> ResetExc ...
9.1579 + (else) -> Conditional (test) -> (stmt)
9.1580 + (body) -> ResetExc ...
9.1581 + (else) -> ...
9.1582 + """
9.1583 +
9.1584 + result = Try(tryexcept, 1, body=[], else_=[], finally_=[])
9.1585 +
9.1586 + if tryexcept.body is not None:
9.1587 + result.body = self.dispatch(tryexcept.body)
9.1588 + if tryexcept.else_ is not None:
9.1589 + result.else_ = self.dispatch(tryexcept.else_)
9.1590 +
9.1591 + results = nodes = []
9.1592 + catch_all = 0
9.1593 +
9.1594 + for spec, assign, stmt in tryexcept.handlers:
9.1595 +
9.1596 + # If no specification exists, produce an unconditional block.
9.1597 +
9.1598 + if spec is None:
9.1599 + nodes += self.dispatch(stmt)
9.1600 + catch_all = 1
9.1601 +
9.1602 + # Produce an exception value check.
9.1603 +
9.1604 + else:
9.1605 + test = Conditional(
9.1606 + isolate_test=1,
9.1607 + test=CheckType(expr=LoadExc(), choices=self._visitTryExcept(spec))
9.1608 + )
9.1609 + test.body = []
9.1610 +
9.1611 + if assign is not None:
9.1612 + test.body.append(
9.1613 + Assign(
9.1614 + code=[
9.1615 + StoreTemp(expr=LoadExc()),
9.1616 + self.dispatch(assign),
9.1617 + ReleaseTemp()
9.1618 + ]
9.1619 + )
9.1620 + )
9.1621 +
9.1622 + test.body += [ResetExc()] + self.dispatch(stmt)
9.1623 + nodes.append(test)
9.1624 + nodes = test.else_ = []
9.1625 +
9.1626 + # Add a raise operation to deal with unhandled exceptions.
9.1627 +
9.1628 + if not catch_all:
9.1629 + nodes.append(
9.1630 + Raise(
9.1631 + expr=LoadExc())
9.1632 + )
9.1633 +
9.1634 + result.handler = results
9.1635 + return result
9.1636 +
9.1637 + def _visitTryExcept(self, spec):
9.1638 +
9.1639 + "Return a list of nodes for the given exception type 'spec'."
9.1640 +
9.1641 + if isinstance(spec, compiler.ast.Tuple):
9.1642 + nodes = []
9.1643 + for node in spec.nodes:
9.1644 + nodes += self._visitTryExcept(node)
9.1645 + else:
9.1646 + nodes = [self.dispatch(spec)]
9.1647 + return nodes
9.1648 +
9.1649 + def visitTryFinally(self, tryfinally):
9.1650 + result = Try(tryfinally, 1, body=[], else_=[], finally_=[])
9.1651 + if tryfinally.body is not None:
9.1652 + result.body = self.dispatch(tryfinally.body)
9.1653 + if tryfinally.final is not None:
9.1654 + result.finally_ = self.dispatch(tryfinally.final)
9.1655 + return result
9.1656 +
9.1657 + def visitTuple(self, tuple):
9.1658 +
9.1659 + "Make a MakeTuple node containing the original 'tuple' contents."
9.1660 +
9.1661 + result = MakeTuple(tuple, 1,
9.1662 + nodes=self.dispatches(tuple.nodes)
9.1663 + )
9.1664 + return result
9.1665 +
9.1666 + def visitUnaryAdd(self, unaryadd):
9.1667 + return self._visitUnary(unaryadd, "__pos__")
9.1668 +
9.1669 + def visitUnarySub(self, unarysub):
9.1670 + return self._visitUnary(unarysub, "__neg__")
9.1671 +
9.1672 + def visitWhile(self, while_):
9.1673 +
9.1674 + """
9.1675 + Make a subprogram for the 'while' node and record its contents inside the
9.1676 + subprogram. Convert...
9.1677 +
9.1678 + While (test) -> (body)
9.1679 + (else)
9.1680 +
9.1681 + ...to:
9.1682 +
9.1683 + Subprogram -> Conditional (test) -> (body) -> Invoke subprogram
9.1684 + (else) -> Conditional (test) -> ReturnFromBlock ...
9.1685 + (else) -> ...
9.1686 + """
9.1687 +
9.1688 + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None)
9.1689 + self.current_subprograms.append(subprogram)
9.1690 +
9.1691 + # Include a conditional statement in the subprogram.
9.1692 + # Inside the conditional, add a recursive invocation to the subprogram
9.1693 + # if the test condition was satisfied.
9.1694 + # Return within the main section of the loop.
9.1695 +
9.1696 + test = Conditional(
9.1697 + test=InvokeFunction(
9.1698 + while_,
9.1699 + expr=LoadAttr(
9.1700 + expr=self.dispatch(while_.test),
9.1701 + name="__bool__"),
9.1702 + ),
9.1703 + body=self.dispatch(while_.body) + [
9.1704 + InvokeRef(
9.1705 + while_,
9.1706 + ref=subprogram
9.1707 + ),
9.1708 + ReturnFromBlock()
9.1709 + ],
9.1710 + else_=[]
9.1711 + )
9.1712 +
9.1713 + # Provide the else section, if present, along with an explicit return.
9.1714 +
9.1715 + if while_.else_ is not None:
9.1716 + test.else_ = self.dispatch(while_.else_) + [ReturnFromBlock()]
9.1717 +
9.1718 + # Finish the subprogram definition.
9.1719 +
9.1720 + subprogram.code = [test]
9.1721 +
9.1722 + self.current_subprograms.pop()
9.1723 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.1724 +
9.1725 + # Make an invocation of the subprogram.
9.1726 +
9.1727 + result = InvokeRef(while_, 1, ref=subprogram)
9.1728 +
9.1729 + # Make nice annotations for the viewer.
9.1730 +
9.1731 + while_._test_call = subprogram.code[0].test
9.1732 +
9.1733 + return result
9.1734 +
9.1735 + # NOTE: Not actually supported.
9.1736 + # NOTE: Virtually the same as visitReturn...
9.1737 +
9.1738 + def visitYield(self, yield_):
9.1739 + result = Yield(yield_, 1,
9.1740 + expr=self.dispatch(yield_.value)
9.1741 + )
9.1742 + return result
9.1743 +
9.1744 + # Convenience methods.
9.1745 +
9.1746 + def _visitBinary(self, binary, left_name, right_name):
9.1747 + return self._visitBinaryOp(binary, self.dispatch(binary.left), self.dispatch(binary.right), left_name, right_name)
9.1748 +
9.1749 + def _visitBinaryCompareOp(self, binary, left, right, left_name, right_name):
9.1750 +
9.1751 + """
9.1752 + Emulate the current mechanisms by producing nodes as follows:
9.1753 +
9.1754 + InvokeRef -> Subprogram -> StoreTemp (expr) -> x.__lt__(y)
9.1755 + Conditional (test) -> __is__(LoadTemp, NotImplemented)
9.1756 + (body) -> ReleaseTemp
9.1757 + StoreTemp (expr) -> y.__gt__(x)
9.1758 + Conditional (test) -> __is__(LoadTemp, NotImplemented)
9.1759 + (body) -> ReturnFromBlock (expr) -> False
9.1760 + (else) -> ReturnFromBlock (expr) -> LoadTemp
9.1761 + (else) -> ReturnFromBlock (expr) -> LoadTemp
9.1762 + """
9.1763 +
9.1764 + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
9.1765 + self.current_subprograms.append(subprogram)
9.1766 +
9.1767 + subprogram.code = [
9.1768 + StoreTemp(
9.1769 + expr=InvokeFunction(
9.1770 + binary,
9.1771 + expr=LoadAttr(expr=left, name=left_name),
9.1772 + args=[right],
9.1773 + star=None,
9.1774 + dstar=None)
9.1775 + ),
9.1776 + Conditional(
9.1777 + isolate_test=1,
9.1778 + test=CheckType(
9.1779 + expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")]
9.1780 + ),
9.1781 + body=[
9.1782 + ReleaseTemp(),
9.1783 + StoreTemp(
9.1784 + expr=InvokeFunction(
9.1785 + binary,
9.1786 + expr=LoadAttr(expr=right, name=right_name),
9.1787 + args=[left],
9.1788 + star=None,
9.1789 + dstar=None)
9.1790 + ),
9.1791 + Conditional(
9.1792 + isolate_test=1,
9.1793 + test=CheckType(
9.1794 + expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")]
9.1795 + ),
9.1796 + body=[
9.1797 + ReturnFromBlock(
9.1798 + expr=LoadName(name="False")
9.1799 + )
9.1800 + ],
9.1801 + else_=[
9.1802 + CheckType(
9.1803 + inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")]
9.1804 + ),
9.1805 + ReturnFromBlock(
9.1806 + expr=LoadTemp()
9.1807 + )
9.1808 + ]
9.1809 + )
9.1810 + ],
9.1811 + else_=[
9.1812 + CheckType(
9.1813 + inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")]
9.1814 + ),
9.1815 + ReturnFromBlock(
9.1816 + expr=LoadTemp()
9.1817 + )
9.1818 + ]
9.1819 + )
9.1820 + ]
9.1821 +
9.1822 + self.current_subprograms.pop()
9.1823 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.1824 +
9.1825 + result = InvokeRef(
9.1826 + binary,
9.1827 + produces_result=1,
9.1828 + ref=subprogram
9.1829 + )
9.1830 +
9.1831 + # Make nice annotations for the viewer.
9.1832 +
9.1833 + binary._left_call = subprogram.code[0].expr
9.1834 + binary._right_call = subprogram.code[1].body[1].expr
9.1835 +
9.1836 + return result
9.1837 +
9.1838 + def _visitBinaryOp(self, binary, left, right, left_name, right_name):
9.1839 +
9.1840 + """
9.1841 + Emulate the current mechanisms by producing nodes as follows:
9.1842 +
9.1843 + InvokeRef -> Subprogram -> Try (body) -> ReturnFromBlock (expr) -> x.__add__(y)
9.1844 + (else)
9.1845 + (handler) -> Conditional (test) -> CheckType (expr) -> LoadExc
9.1846 + (choices) -> LoadName TypeError
9.1847 + (body) -> ReturnFromBlock (expr) -> y.__radd__(x)
9.1848 + (else)
9.1849 + """
9.1850 +
9.1851 + subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)
9.1852 + self.current_subprograms.append(subprogram)
9.1853 +
9.1854 + subprogram.code = [
9.1855 + Try(binary, 1,
9.1856 + body=[
9.1857 + ReturnFromBlock(
9.1858 + expr=InvokeFunction(
9.1859 + binary,
9.1860 + expr=LoadAttr(expr=left, name=left_name),
9.1861 + args=[right],
9.1862 + star=None,
9.1863 + dstar=None)
9.1864 + )
9.1865 + ],
9.1866 + else_=[],
9.1867 + finally_=[],
9.1868 + handler=[
9.1869 + Conditional(
9.1870 + test=CheckType(expr=LoadExc(), choices=[LoadName(name="TypeError")]),
9.1871 + body=[
9.1872 + ReturnFromBlock(
9.1873 + expr=InvokeFunction(
9.1874 + binary,
9.1875 + expr=LoadAttr(expr=right, name=right_name),
9.1876 + args=[left],
9.1877 + star=None,
9.1878 + dstar=None)
9.1879 + )
9.1880 + ],
9.1881 + else_=[]
9.1882 + )
9.1883 + ]
9.1884 + )
9.1885 + ]
9.1886 +
9.1887 + self.current_subprograms.pop()
9.1888 + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram
9.1889 +
9.1890 + result = InvokeRef(
9.1891 + binary,
9.1892 + produces_result=1,
9.1893 + ref=subprogram
9.1894 + )
9.1895 +
9.1896 + # Make nice annotations for the viewer.
9.1897 +
9.1898 + binary._left_call = subprogram.code[0].body[0].expr
9.1899 + binary._right_call = subprogram.code[0].handler[0].body[0].expr
9.1900 +
9.1901 + return result
9.1902 +
9.1903 + def _visitBuiltin(self, builtin, name):
9.1904 + result = InvokeFunction(builtin, 1, expr=LoadName(name=name), args=self.dispatches(builtin.nodes))
9.1905 + return result
9.1906 +
9.1907 + def _visitUnary(self, unary, name):
9.1908 + result = InvokeFunction(unary, 1,
9.1909 + expr=LoadAttr(
9.1910 + expr=self.dispatch(unary.expr),
9.1911 + name=name
9.1912 + )
9.1913 + )
9.1914 +
9.1915 + # Make nice annotations for the viewer.
9.1916 +
9.1917 + unary._unary_call = result
9.1918 +
9.1919 + return result
9.1920 +
9.1921 +# Convenience functions.
9.1922 +
9.1923 +def simplify(filename, builtins=0, module_name=None):
9.1924 +
9.1925 + """
9.1926 + Simplify the module stored in the file with the given 'filename'.
9.1927 +
9.1928 + If the optional 'builtins' parameter is set to a true value (the default
9.1929 + being a false value), then the module is considered as the builtins module.
9.1930 + """
9.1931 +
9.1932 + simplifier = Simplifier(builtins)
9.1933 + module = compiler.parseFile(filename)
9.1934 + compiler.misc.set_filename(filename, module)
9.1935 + if builtins:
9.1936 + name = module_name or "__builtins__"
9.1937 + else:
9.1938 + path, ext = os.path.splitext(filename)
9.1939 + path, name = os.path.split(path)
9.1940 + name = module_name or name
9.1941 + simplified = simplifier.process(module, name)
9.1942 + return simplified
9.1943 +
9.1944 +# vim: tabstop=4 expandtab shiftwidth=4
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/simplify/annotate.py Sun May 27 18:25:25 2007 +0200
10.3 @@ -0,0 +1,1789 @@
10.4 +#!/usr/bin/env python
10.5 +
10.6 +"""
10.7 +Annotate program node structures. The code in this module operates upon nodes
10.8 +which are produced when simplifying AST node trees originating from the compiler
10.9 +module.
10.10 +
10.11 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
10.12 +
10.13 +This software is free software; you can redistribute it and/or
10.14 +modify it under the terms of the GNU General Public License as
10.15 +published by the Free Software Foundation; either version 2 of
10.16 +the License, or (at your option) any later version.
10.17 +
10.18 +This software is distributed in the hope that it will be useful,
10.19 +but WITHOUT ANY WARRANTY; without even the implied warranty of
10.20 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10.21 +GNU General Public License for more details.
10.22 +
10.23 +You should have received a copy of the GNU General Public
10.24 +License along with this library; see the file LICENCE.txt
10.25 +If not, write to the Free Software Foundation, Inc.,
10.26 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
10.27 +
10.28 +--------
10.29 +
10.30 +To use this module, the easiest approach is to use the load function:
10.31 +
10.32 +load(filename, builtins)
10.33 +
10.34 +To control module importing, an importer should be constructed and employed.
10.35 +Here, the standard path for module searching is used:
10.36 +
10.37 +importer = Importer(sys.path)
10.38 +load(filename, builtins, importer)
10.39 +
10.40 +Underneath the load function, the annotate function provides support for
10.41 +annotating modules already processed by simplify and fixnames:
10.42 +
10.43 +annotate(module, builtins)
10.44 +
10.45 +And at the most basic level, the most intricate approach involves obtaining an
10.46 +Annotator object:
10.47 +
10.48 +annotator = Annotator()
10.49 +
10.50 +Then, processing an existing module with it:
10.51 +
10.52 +annotator.process(module)
10.53 +
10.54 +If a module containing built-in classes and functions has already been
10.55 +annotated, such a module should be passed in as an additional argument:
10.56 +
10.57 +annotator.process(module, builtins)
10.58 +"""
10.59 +
10.60 +from simplify.simplified import *
10.61 +import simplify, simplify.fixnames # for the load function
10.62 +import compiler
10.63 +import os
10.64 +
10.65 +class System:
10.66 +
10.67 + """
10.68 + A class maintaining the state of the annotation system. When the system
10.69 + counter can no longer be incremented by any annotation operation, the
10.70 + system may be considered stable and fully annotated.
10.71 + """
10.72 +
10.73 + def __init__(self):
10.74 + self.count = 0
10.75 +
10.76 + def init(self, node, attr="types"):
10.77 +
10.78 + "Initialise a 'node' for annotation."
10.79 +
10.80 + if not hasattr(node, attr):
10.81 + setattr(node, attr, set())
10.82 +
10.83 + def annotate(self, node, types, attr="types"):
10.84 +
10.85 + "Annotate the given 'node' with the given 'types'."
10.86 +
10.87 + self.init(node, attr)
10.88 + self.combine(getattr(node, attr), types)
10.89 +
10.90 + def combine(self, target, types):
10.91 +
10.92 + """
10.93 + Combine the 'target' list with the given 'types', counting new members.
10.94 + """
10.95 +
10.96 + for type in types:
10.97 + if type not in target:
10.98 + target.add(type)
10.99 + self.count += 1
10.100 +
10.101 +system = System()
10.102 +
10.103 +# Exceptions.
10.104 +
10.105 +class AnnotationError(SimplifiedError):
10.106 +
10.107 + "An error in the annotation process."
10.108 +
10.109 + pass
10.110 +
10.111 +class AnnotationMessage(Exception):
10.112 +
10.113 + "A lesser annotation error."
10.114 +
10.115 + pass
10.116 +
10.117 +# Annotation.
10.118 +
10.119 +class Annotator(Visitor):
10.120 +
10.121 + """
10.122 + The type annotator which traverses the program nodes, typically depth-first,
10.123 + and maintains a record of the current set of types applying to the currently
10.124 + considered operation. Such types are also recorded on the nodes, and a
10.125 + special "system" record is maintained to monitor the level of annotation
10.126 + activity with a view to recognising when no more annotations are possible.
10.127 +
10.128 + Throughout the annotation activity, type information consists of lists of
10.129 + Attribute objects where such objects retain information about the context of
10.130 + the type (since a value in the program may be associated with an object or
10.131 + class) and the actual type of the value being manipulated. Upon accessing
10.132 + attribute information on namespaces, additional accessor information is also
10.133 + exchanged - this provides a means of distinguishing between the different
10.134 + types possible when the means of constructing the namespace may depend on
10.135 + run-time behaviour.
10.136 +
10.137 + Covered: Assign, CheckType, Conditional, Global, Import, InvokeRef,
10.138 + InvokeFunction, LoadAttr, LoadExc, LoadName, LoadRef, LoadTemp,
10.139 + Module, Not, Pass, Raise, ReleaseTemp, ReturnFromBlock,
10.140 + ReturnFromFunction, StoreAttr, StoreName, StoreTemp, Subprogram,
10.141 + Try.
10.142 + """
10.143 +
10.144 + def __init__(self, importer=None):
10.145 +
10.146 + "Initialise the visitor with an optional 'importer'."
10.147 +
10.148 + Visitor.__init__(self)
10.149 + self.system = system
10.150 + self.importer = importer or Importer()
10.151 +
10.152 + # Satisfy visitor issues.
10.153 +
10.154 + self.visitor = self
10.155 +
10.156 + def process(self, module, builtins=None):
10.157 +
10.158 + """
10.159 + Process the given 'module', using the optional 'builtins' to access
10.160 + built-in classes and functions.
10.161 + """
10.162 +
10.163 + self.subprograms = []
10.164 + self.current_subprograms = []
10.165 + self.current_namespaces = []
10.166 + self.rerun_subprograms = {}
10.167 + self.namespace = None
10.168 + self.module = module
10.169 +
10.170 + # Process the module, supplying builtins if possible.
10.171 +
10.172 + self.builtins = builtins
10.173 + self.global_namespace = Namespace()
10.174 +
10.175 + if builtins is not None:
10.176 + self.builtins_namespace = builtins.namespace
10.177 + else:
10.178 + self.builtins_namespace = self.global_namespace
10.179 +
10.180 + # NOTE: Not declaring module namespace usage, even though it is used.
10.181 +
10.182 + self.process_node(module, self.global_namespace, 0)
10.183 +
10.184 + def process_node(self, node, locals, using_module_namespace):
10.185 +
10.186 + """
10.187 + Process a subprogram or module 'node', indicating the initial 'locals'.
10.188 + Note that this method may mutate nodes in the original program.
10.189 + """
10.190 +
10.191 + # Recursion test.
10.192 +
10.193 + if node in self.current_subprograms:
10.194 + if not self.rerun_subprograms.has_key(node):
10.195 + self.rerun_subprograms[node] = []
10.196 + self.rerun_subprograms[node].append(locals)
10.197 + return
10.198 +
10.199 + # Record the current subprogram and namespace.
10.200 +
10.201 + self.current_subprograms.append(node)
10.202 +
10.203 + # Determine the namespace.
10.204 +
10.205 + self.current_namespaces.append(self.namespace)
10.206 + self.namespace = locals
10.207 +
10.208 + # Add namespace details to any structure involved.
10.209 +
10.210 + if getattr(node, "structure", None) is not None:
10.211 + node.structure.namespace = Namespace()
10.212 +
10.213 + # Initialise bases where appropriate.
10.214 +
10.215 + if hasattr(node.structure, "bases"):
10.216 + base_refs = []
10.217 + for base in node.structure.bases:
10.218 + self.dispatch(base)
10.219 + base_refs.append(self.namespace.types)
10.220 + node.structure.base_refs = base_refs
10.221 +
10.222 + # Dispatch to the code itself.
10.223 +
10.224 + node.namespace = self.namespace
10.225 + self.set_module_namespace(using_module_namespace)
10.226 +
10.227 + self.dispatch(node)
10.228 + self.extract_results(node)
10.229 +
10.230 + while self.rerun_subprograms.has_key(node):
10.231 + all_rerun_locals = self.rerun_subprograms[node]
10.232 + del self.rerun_subprograms[node]
10.233 + for rerun_locals in all_rerun_locals:
10.234 + #print "Re-running", node, "with", rerun_locals
10.235 +
10.236 + self.namespace = rerun_locals
10.237 + node.namespace = rerun_locals
10.238 + self.set_module_namespace(using_module_namespace)
10.239 +
10.240 + self.dispatch(node)
10.241 + self.extract_results(node)
10.242 +
10.243 + # Restore the previous subprogram and namespace.
10.244 +
10.245 + self.namespace = self.current_namespaces.pop()
10.246 + self.current_subprograms.pop()
10.247 + self.reset_module_namespace(using_module_namespace)
10.248 +
10.249 + def set_module_namespace(self, using_module_namespace):
10.250 +
10.251 + """
10.252 + In order to keep global accesses working, the module namespace must be
10.253 + adjusted.
10.254 + """
10.255 +
10.256 + if using_module_namespace:
10.257 + self.module.namespace = self.namespace
10.258 +
10.259 + def reset_module_namespace(self, using_module_namespace):
10.260 +
10.261 + """
10.262 + In order to keep global accesses working, the module namespace must be
10.263 + reset.
10.264 + """
10.265 +
10.266 + if using_module_namespace:
10.267 + self.module.namespace = self.namespace
10.268 +
10.269 + def extract_results(self, node):
10.270 +
10.271 + "Extract results from the namespace."
10.272 +
10.273 + node.namespace = self.namespace
10.274 + self.system.annotate(node, self.namespace.raises, "raises")
10.275 + self.system.annotate(node, self.namespace.returns, "returns")
10.276 + if hasattr(node, "return_locals"):
10.277 + node.return_locals.update(self.namespace.return_locals)
10.278 +
10.279 + def annotate(self, node, types=None):
10.280 +
10.281 + """
10.282 + Annotate the given 'node' in the system, using either the optional
10.283 + 'types' or the namespace's current type information.
10.284 + """
10.285 +
10.286 + if types is None:
10.287 + self.system.annotate(node, self.namespace.types)
10.288 + else:
10.289 + self.system.annotate(node, types)
10.290 +
10.291 + def annotate_parameters(self, node, items):
10.292 +
10.293 + """
10.294 + Annotate the given 'node' using the given 'items' and updating the
10.295 + system's annotation counter.
10.296 + """
10.297 +
10.298 + if not hasattr(node, "paramtypes"):
10.299 + node.paramtypes = {}
10.300 +
10.301 + for param, types in items:
10.302 + if not node.paramtypes.has_key(param):
10.303 + node.paramtypes[param] = set()
10.304 + self.system.combine(node.paramtypes[param], types)
10.305 +
10.306 + # Visitor methods.
10.307 +
10.308 + def default(self, node):
10.309 +
10.310 + """
10.311 + Process the given 'node', given that it does not have a specific
10.312 + handler.
10.313 + """
10.314 +
10.315 + raise AnnotationMessage, "Node '%s' not supported." % node
10.316 +
10.317 + def dispatch(self, node, *args):
10.318 + try:
10.319 + Visitor.dispatch(self, node, *args)
10.320 + except AnnotationError, exc:
10.321 + exc.add(node)
10.322 + raise
10.323 + except AnnotationMessage, exc:
10.324 + raise AnnotationError(exc, node)
10.325 +
10.326 + # Specific node methods.
10.327 +
10.328 + def visitAssign(self, assign):
10.329 +
10.330 + """
10.331 + Process the 'assign' node and its contents.
10.332 + """
10.333 +
10.334 + self.dispatches(assign.code)
10.335 +
10.336 + def visitCheckType(self, checktype):
10.337 +
10.338 + """
10.339 + Process the 'checktype' node, finding the possible types of the
10.340 + exception, and processing each choice to build a list of checked types
10.341 + for the exception.
10.342 + """
10.343 +
10.344 + inverted = getattr(checktype, "inverted", 0)
10.345 + self.dispatch(checktype.expr)
10.346 +
10.347 + expr_types = self.namespace.types
10.348 + choice_types = set()
10.349 + choices = []
10.350 +
10.351 + for choice in checktype.choices:
10.352 + choices.append(self.dispatch(choice))
10.353 + choice_types.update(self.namespace.types)
10.354 +
10.355 + for expr_type in expr_types:
10.356 + in_choices = expr_type.type.get_class() in choice_types
10.357 +
10.358 + # Filter out types not in the choices list unless the operation is
10.359 + # inverted; in which case, filter out types in the choices list.
10.360 +
10.361 + if not inverted and not in_choices or inverted and in_choices:
10.362 + self._prune_non_accesses(checktype.expr, expr_type)
10.363 +
10.364 + def visitConditional(self, conditional):
10.365 +
10.366 + """
10.367 + Process the 'conditional' node, processing the test, body and else
10.368 + clauses and recording their processed forms. The body and else clauses
10.369 + are processed within their own namespaces, and the test is also
10.370 + processed in its own namespace if 'isolate_test' is set on the
10.371 + 'conditional' node.
10.372 + """
10.373 +
10.374 + # Conditionals keep local namespace changes isolated.
10.375 + # With Return nodes inside the body/else sections, the changes are
10.376 + # communicated to the caller.
10.377 +
10.378 + is_module = self.namespace is self.module.namespace
10.379 +
10.380 + # Where the test is closely associated with the body, save the namespace
10.381 + # before entering the test.
10.382 +
10.383 + if conditional.isolate_test:
10.384 + saved_namespace = self.namespace
10.385 + self.namespace = Namespace()
10.386 + if is_module:
10.387 + self.module.namespace = self.namespace
10.388 + self.namespace.merge_namespace(saved_namespace)
10.389 +
10.390 + self.dispatch(conditional.test)
10.391 +
10.392 + # Where the test may affect the body and the else clause, save the
10.393 + # namespace after processing the test.
10.394 +
10.395 + if not conditional.isolate_test:
10.396 + saved_namespace = self.namespace
10.397 + self.namespace = Namespace()
10.398 + if is_module:
10.399 + self.module.namespace = self.namespace
10.400 + self.namespace.merge_namespace(saved_namespace)
10.401 +
10.402 + # NOTE: Exception recording.
10.403 +
10.404 + else:
10.405 + test_raises = set()
10.406 + test_raises.update(self.namespace.raises)
10.407 +
10.408 + # Process the body clause.
10.409 +
10.410 + self.dispatches(conditional.body)
10.411 + body_namespace = self.namespace
10.412 +
10.413 + # Use the saved namespace as a template for the else clause.
10.414 +
10.415 + self.namespace = Namespace()
10.416 + if is_module:
10.417 + self.module.namespace = self.namespace
10.418 + self.namespace.merge_namespace(saved_namespace)
10.419 +
10.420 + # Process the else clause.
10.421 +
10.422 + self.dispatches(conditional.else_)
10.423 + else_namespace = self.namespace
10.424 +
10.425 + # Merge the body and else namespaces.
10.426 +
10.427 + self.namespace = Namespace()
10.428 + if is_module:
10.429 + self.module.namespace = self.namespace
10.430 + self.namespace.merge_namespace(body_namespace)
10.431 + self.namespace.merge_namespace(else_namespace)
10.432 +
10.433 + # NOTE: Test of exception type pruning based on the test/body.
10.434 + # Note that the checked exceptions are tested for re-raising.
10.435 +
10.436 + if conditional.isolate_test:
10.437 + for exc_type in test_raises:
10.438 + if exc_type not in body_namespace.raises:
10.439 + self.namespace.revoke_exception_type(exc_type)
10.440 +
10.441 + def visitGlobal(self, global_):
10.442 +
10.443 + """
10.444 + Leave the 'global_' node unprocessed since namespaces should have
10.445 + already been altered to take global names into consideration.
10.446 + """
10.447 +
10.448 + pass
10.449 +
10.450 + def visitImport(self, import_):
10.451 +
10.452 + """
10.453 + Process the 'import_' node, importing the module with the stated name
10.454 + and storing details on the node.
10.455 + """
10.456 +
10.457 + module = self.importer.load(import_.name, self.builtins, getattr(import_, "alias", None))
10.458 + if module is not None:
10.459 + self.namespace.set_types(set([module]))
10.460 + else:
10.461 + self.namespace.set_types(set())
10.462 + self.annotate(import_) # mainly for viewing purposes
10.463 +
10.464 + def _visitInvoke(self, invoke, invocation_types, have_args):
10.465 +
10.466 + """
10.467 + Process the 'invoke' node, using the given 'invocation_types' as the
10.468 + list of callables to be investigated for instantiation or for the
10.469 + invocation of functions or blocks. If 'have_args' is a true value, any
10.470 + invocation or instantiation will involve arguments.
10.471 + """
10.472 +
10.473 + # Now locate and invoke the subprogram. This can be complicated because
10.474 + # the target may be a class or object, and there may be many different
10.475 + # related subprograms.
10.476 +
10.477 + invocations = []
10.478 +
10.479 + # Visit each callable in turn, finding subprograms.
10.480 +
10.481 + for attr in invocation_types:
10.482 +
10.483 + # Deal with class invocations by providing instance objects.
10.484 + # Here, each class is queried for the __init__ method, which may
10.485 + # exist for some combinations of classes in a hierarchy but not for
10.486 + # others.
10.487 +
10.488 + if isinstance(attr.type, Class):
10.489 + attributes = get_attributes(attr.type, "__init__")
10.490 +
10.491 + # Deal with object invocations by using __call__ methods.
10.492 +
10.493 + elif isinstance(attr.type, Instance):
10.494 + attributes = get_attributes(attr.type, "__call__")
10.495 +
10.496 + # Normal functions or methods are more straightforward.
10.497 + # Here, we model them using an attribute with no context and with
10.498 + # no associated accessor.
10.499 +
10.500 + else:
10.501 + attributes = [(attr, None)]
10.502 +
10.503 + # Inspect each attribute and extract the subprogram.
10.504 +
10.505 + for attribute, accessor in attributes:
10.506 +
10.507 + # If a class is involved, presume that it must create a new
10.508 + # object.
10.509 +
10.510 + if isinstance(attr.type, Class):
10.511 +
10.512 + # Instantiate the class.
10.513 +
10.514 + instance = self.new_instance(invoke, attr.type)
10.515 +
10.516 + # For instantiations, switch the context.
10.517 +
10.518 + if attribute is not None:
10.519 + attribute = Attribute(instance, attribute.type)
10.520 +
10.521 + # Request an instance-specific initialiser.
10.522 +
10.523 + attribute = attr.type.get_attribute_for_instance(attribute, instance)
10.524 +
10.525 + # Skip cases where no callable is found.
10.526 +
10.527 + if attribute is not None:
10.528 +
10.529 + # If a subprogram is defined, invoke it.
10.530 +
10.531 + self.invoke_subprogram(invoke, attribute)
10.532 + if attribute.type not in invocations:
10.533 + invocations.append(attribute.type)
10.534 +
10.535 + elif not isinstance(attr.type, Class):
10.536 + print "Invocation type is None for", accessor
10.537 +
10.538 + else:
10.539 +
10.540 + # Test to see if no arguments were supplied in cases where no
10.541 + # initialiser was found.
10.542 +
10.543 + if have_args:
10.544 + raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type
10.545 +
10.546 + # Special case: initialisation.
10.547 +
10.548 + if isinstance(attr.type, Class):
10.549 +
10.550 + # Associate the instance with the result of this invocation.
10.551 +
10.552 + self.namespace.set_types(set([Attribute(None, instance)]))
10.553 + self.annotate(invoke)
10.554 +
10.555 + # Remember the invocations that were found, along with the return type
10.556 + # information.
10.557 +
10.558 + invoke.invocations = invocations
10.559 + self.namespace.set_types(getattr(invoke, "types", set()))
10.560 +
10.561 + def visitInvokeRef(self, invoke):
10.562 +
10.563 + """
10.564 + Process the 'invoke' node, first finding the callables indicated by the
10.565 + reference.
10.566 + """
10.567 +
10.568 + # Where the invocation belongs to an instance but the invoked subprogram
10.569 + # does not, request a special copy.
10.570 +
10.571 + instance = getattr(invoke, "instance", None)
10.572 + if instance is not None and getattr(invoke.ref, "instance", None) is None:
10.573 + if invoke.ref.copies.has_key(instance):
10.574 + invoke.ref = invoke.ref.copies[instance]
10.575 + else:
10.576 + invoke.ref = invoke.ref.copy(instance)
10.577 + #print "Created", invoke.ref, "for", getattr(invoke.ref, "instance", None)
10.578 + invoke.ref.module.simplifier.subnames[invoke.ref.full_name()] = invoke.ref
10.579 + invocation_types = [Attribute(None, invoke.ref)]
10.580 + self._visitInvoke(invoke, invocation_types, have_args=0)
10.581 +
10.582 + def visitInvokeFunction(self, invoke):
10.583 +
10.584 + """
10.585 + Process the 'invoke' node, first finding the callables indicated by the
10.586 + expression.
10.587 + """
10.588 +
10.589 + self.dispatch(invoke.expr)
10.590 + invocation_types = self.namespace.types
10.591 +
10.592 + # Invocation processing starts with making sure that the arguments have
10.593 + # been processed.
10.594 +
10.595 + self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke))
10.596 +
10.597 + def visitLoadAttr(self, loadattr):
10.598 +
10.599 + """
10.600 + Process the 'loadattr' node, processing and storing the expression, and
10.601 + using the expression's types to construct records of accesses and
10.602 + non-accesses using the stated attribute name.
10.603 + """
10.604 +
10.605 + self.dispatch(loadattr.expr)
10.606 + types = set()
10.607 + raises = set()
10.608 + non_accesses = []
10.609 + accesses = {}
10.610 +
10.611 + # For each expression type...
10.612 +
10.613 + for attr in self.namespace.types:
10.614 +
10.615 + # Find types for the named attribute.
10.616 +
10.617 + attributes = get_attributes(attr.type, loadattr.name)
10.618 +
10.619 + # Where no attributes exist...
10.620 +
10.621 + if not attributes:
10.622 +
10.623 + # Register new invalid accesses and mark a possible exception.
10.624 +
10.625 + if not attr in non_accesses:
10.626 + non_accesses.append(attr)
10.627 + exc = self.get_builtin_instances(loadattr, "AttributeError")
10.628 + raises.update(exc)
10.629 + self.namespace.raises.update(exc)
10.630 +
10.631 + # Revoke this type from any name involved.
10.632 +
10.633 + self._prune_non_accesses(loadattr.expr, attr)
10.634 +
10.635 + # For each type found...
10.636 +
10.637 + for attribute, accessor in attributes:
10.638 +
10.639 + # For actual attributes, register the type and remember the
10.640 + # access.
10.641 +
10.642 + if attribute is not None:
10.643 + types.add(attribute)
10.644 + if not accesses.has_key(attr.type):
10.645 + accesses[attr.type] = []
10.646 + if not (attribute, accessor) in accesses[attr.type]:
10.647 + accesses[attr.type].append((attribute, accessor))
10.648 +
10.649 + # Otherwise, register new invalid accesses and note a possible
10.650 + # exception.
10.651 +
10.652 + else:
10.653 + if not attr in non_accesses:
10.654 + non_accesses.append(attr)
10.655 + exc = self.get_builtin_instances(loadattr, "AttributeError")
10.656 + raises.update(exc)
10.657 + self.namespace.raises.update(exc)
10.658 +
10.659 + # Revoke this type from any name involved.
10.660 +
10.661 + self._prune_non_accesses(loadattr.expr, attr)
10.662 +
10.663 + if not types:
10.664 + print "No attribute found for", loadattr.name, "given", self.namespace.types
10.665 +
10.666 + # Remember the result types.
10.667 +
10.668 + self.namespace.set_types(types)
10.669 + loadattr.non_accesses = non_accesses
10.670 + loadattr.accesses = accesses
10.671 + loadattr.raises = raises
10.672 + self.annotate(loadattr)
10.673 +
10.674 + def _prune_non_accesses(self, expr, attr):
10.675 +
10.676 + """
10.677 + Prune type information from 'expr' where the given 'attr' has been
10.678 + shown to be a non-access.
10.679 + """
10.680 +
10.681 + if isinstance(expr, LoadName):
10.682 + self.namespace.revoke(expr.name, attr)
10.683 + elif isinstance(expr, LoadExc):
10.684 + self.namespace.revoke_exception_type(attr)
10.685 + elif isinstance(expr, LoadTemp):
10.686 + self.namespace.revoke_temp_type(getattr(expr, "index", None), attr)
10.687 +
10.688 + # LoadAttr cannot be pruned since this might unintentionally prune
10.689 + # legitimate types from other applications of the referenced type, it
10.690 + # almost certainly doesn't take "concurrent" mutation into
10.691 + # consideration (where in a running program, the pruned type is actually
10.692 + # reintroduced, making the pruning invalid), and there is no easy way of
10.693 + # preserving the meaning of a namespace without either creating lots of
10.694 + # specialised instances, and even then...
10.695 +
10.696 + #elif isinstance(expr, LoadAttr):
10.697 + # for expr_attr in expr.expr.types:
10.698 + # if hasattr(expr_attr.type, "namespace"):
10.699 + # expr_attr.type.namespace.revoke(expr.name, attr)
10.700 +
10.701 + def visitLoadExc(self, loadexc):
10.702 +
10.703 + """
10.704 + Process the 'loadexc' node, discovering the possible exception types
10.705 + raised.
10.706 + """
10.707 +
10.708 + self.namespace.set_types(self.namespace.raises)
10.709 + self.annotate(loadexc)
10.710 +
10.711 + def visitLoadName(self, loadname):
10.712 +
10.713 + """
10.714 + Process the 'loadname' node, processing the name information on the node
10.715 + to determine which types are involved with the name.
10.716 + """
10.717 +
10.718 + self.namespace.set_types(self.namespace.load(loadname.name))
10.719 + self.annotate(loadname)
10.720 +
10.721 + def visitLoadRef(self, loadref):
10.722 +
10.723 + """
10.724 + Process the 'loadref' node, obtaining type information about the
10.725 + reference stated on the node.
10.726 + """
10.727 +
10.728 + self.namespace.set_types(set([Attribute(None, loadref.ref)]))
10.729 + self.annotate(loadref)
10.730 +
10.731 + def visitLoadTemp(self, loadtemp):
10.732 +
10.733 + """
10.734 + Process the 'loadtemp' node, obtaining type information about the
10.735 + temporary variable accessed, and removing variable information where the
10.736 + 'release' attribute has been set on the node.
10.737 + """
10.738 +
10.739 + index = getattr(loadtemp, "index", None)
10.740 + try:
10.741 + if getattr(loadtemp, "release", 0):
10.742 + self.namespace.set_types(self.namespace.temp[index].pop())
10.743 + else:
10.744 + self.namespace.set_types(self.namespace.temp[index][-1])
10.745 + except KeyError:
10.746 + raise AnnotationMessage, "Temporary store index '%s' not defined." % index
10.747 + self.annotate(loadtemp)
10.748 +
10.749 + def visitMakeTuple(self, maketuple):
10.750 +
10.751 + """
10.752 + Process the 'maketuple' node and its contents.
10.753 + """
10.754 +
10.755 + # Get a tuple and populate it with type information for the contents.
10.756 +
10.757 + tuples = self.get_builtin_instances(maketuple, "tuple")
10.758 +
10.759 + # NOTE: This is dependent on the tuple definition in the builtins.
10.760 +
10.761 + for node in maketuple.nodes:
10.762 + self.dispatch(node)
10.763 + for t in tuples:
10.764 + t.type.namespace.add("value", self.namespace.types)
10.765 +
10.766 + self.namespace.set_types(tuples)
10.767 + self.annotate(maketuple)
10.768 +
10.769 + def visitModule(self, module):
10.770 +
10.771 + """
10.772 + Process the 'module' and its contents.
10.773 + """
10.774 +
10.775 + self.dispatches(module.code)
10.776 +
10.777 + def visitNot(self, not_):
10.778 +
10.779 + "Process the 'not_' node and its expression."
10.780 +
10.781 + self.dispatch(not_.expr)
10.782 +
10.783 + def visitPass(self, pass_):
10.784 +
10.785 + "Leave the 'pass_' node unprocessed."
10.786 +
10.787 + pass
10.788 +
10.789 + def visitRaise(self, raise_):
10.790 +
10.791 + """
10.792 + Process the 'raise_' node, processing any traceback information along
10.793 + with the raised exception expression, converting the node into a kind of
10.794 + invocation where the expression is found not to be an invocation itself.
10.795 + This node affects the namespace, adding exception types to the list of
10.796 + those raised in the namespace.
10.797 + """
10.798 +
10.799 + if getattr(raise_, "traceback", None) is not None:
10.800 + self.dispatch(raise_.traceback)
10.801 + self.dispatch(raise_.expr)
10.802 +
10.803 + # Handle bare name exceptions by converting any classes to instances.
10.804 +
10.805 + if not isinstance(raise_.expr, InvokeFunction):
10.806 + raise_.pos_args = []
10.807 + raise_.kw_args = {}
10.808 + raise_.star = None
10.809 + raise_.dstar = None
10.810 + types = set()
10.811 + for attr in self.namespace.types:
10.812 + if isinstance(attr.type, Class):
10.813 + self._visitInvoke(raise_, [attr], have_args=0)
10.814 + types.update(self.namespace.types)
10.815 + else:
10.816 + types = self.namespace.types
10.817 +
10.818 + self.namespace.raises.update(types)
10.819 +
10.820 + def visitReleaseTemp(self, releasetemp):
10.821 +
10.822 + """
10.823 + Process the 'releasetemp' node, removing temporary variable information
10.824 + from the current namespace.
10.825 + """
10.826 +
10.827 + index = getattr(releasetemp, "index", None)
10.828 + try:
10.829 + self.namespace.temp[index].pop()
10.830 + except KeyError:
10.831 + raise AnnotationMessage, "Temporary store index '%s' not defined." % index
10.832 + except IndexError:
10.833 + pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index
10.834 +
10.835 + def visitResetExc(self, resetexc):
10.836 + self.namespace.raises = set()
10.837 +
10.838 + def visitReturn(self, return_):
10.839 +
10.840 + """
10.841 + Process the 'return_' node, processing any expression and obtaining type
10.842 + information to be accumulated in the current namespace's list of return
10.843 + types. A snapshot of the namespace is taken for the purposes of
10.844 + reconciling or merging namespaces where subprograms actually share
10.845 + locals with their callers.
10.846 + """
10.847 +
10.848 + if hasattr(return_, "expr"):
10.849 + self.dispatch(return_.expr)
10.850 + self.namespace.returns.update(self.namespace.types)
10.851 + self.annotate(return_)
10.852 + self.namespace.snapshot()
10.853 +
10.854 + visitReturnFromBlock = visitReturn
10.855 + visitReturnFromFunction = visitReturn
10.856 +
10.857 + def visitStoreAttr(self, storeattr):
10.858 +
10.859 + """
10.860 + Process the 'storeattr' node, processing the expression and target, and
10.861 + using the type information obtained to build records of legitimate
10.862 + writes to the stated attribute, along with "impossible" non-writes to
10.863 + the attribute.
10.864 + """
10.865 +
10.866 + self.dispatch(storeattr.expr)
10.867 + expr = self.namespace.types
10.868 + self.dispatch(storeattr.lvalue)
10.869 + writes = {}
10.870 + non_writes = []
10.871 + for attr in self.namespace.types:
10.872 + # NOTE: Impose "atomic" constraints on certain types.
10.873 + if attr is None:
10.874 + if not attr in non_writes:
10.875 + non_writes.append(attr)
10.876 + continue
10.877 + attr.type.namespace.add(storeattr.name, expr)
10.878 + writes[attr.type] = attr.type.namespace.load(storeattr.name)
10.879 + if not writes:
10.880 + print "Unable to store attribute", storeattr.name, "given", self.namespace.types
10.881 + storeattr.writes = writes
10.882 + storeattr.non_writes = non_writes
10.883 +
10.884 + def visitStoreName(self, storename):
10.885 +
10.886 + """
10.887 + Process the 'storename' node, processing the expression on the node and
10.888 + associating the type information obtained with the stated name in the
10.889 + current namespace.
10.890 + """
10.891 +
10.892 + self.dispatch(storename.expr)
10.893 + self.namespace.store(storename.name, self.namespace.types)
10.894 + self.annotate(storename)
10.895 +
10.896 + def visitStoreTemp(self, storetemp):
10.897 +
10.898 + """
10.899 + Process the 'storetemp' node, processing the expression on the node and
10.900 + associating the type information obtained with a temporary variable in
10.901 + the current namespace.
10.902 + """
10.903 +
10.904 + self.dispatch(storetemp.expr)
10.905 + index = getattr(storetemp, "index", None)
10.906 + if not self.namespace.temp.has_key(index):
10.907 + self.namespace.temp[index] = []
10.908 + self.namespace.temp[index].append(self.namespace.types)
10.909 +
10.910 + def visitSubprogram(self, subprogram):
10.911 +
10.912 + """
10.913 + Process the 'subprogram' node, processing its contents (a group of nodes
10.914 + comprising the subprogram).
10.915 + """
10.916 +
10.917 + self.dispatches(subprogram.code)
10.918 +
10.919 + def visitTry(self, try_):
10.920 +
10.921 + """
10.922 + Process the 'try_' node, processing the body clause in its own namespace
10.923 + derived from the current namespace, processing any handler clause using
10.924 + the namespace information accumulated in the body, and processing any
10.925 + else and finally clauses, attempting to supply each with appropriate
10.926 + namespace information.
10.927 + """
10.928 +
10.929 + is_module = self.namespace is self.module.namespace
10.930 +
10.931 + self.dispatches(try_.body)
10.932 +
10.933 + # Save the namespace from the body.
10.934 +
10.935 + body_namespace = Namespace()
10.936 + body_namespace.merge_namespace(self.namespace)
10.937 +
10.938 + # Process the handler.
10.939 +
10.940 + if hasattr(try_, "handler"):
10.941 + self.dispatches(try_.handler)
10.942 +
10.943 + # Save the namespace from the handler.
10.944 +
10.945 + handler_namespace = Namespace()
10.946 + handler_namespace.merge_namespace(self.namespace)
10.947 +
10.948 + # Remember the raised exceptions encountered so far.
10.949 +
10.950 + raises = self.namespace.raises
10.951 +
10.952 + # Process the else clause.
10.953 +
10.954 + if hasattr(try_, "else_"):
10.955 +
10.956 + # Restore the body namespace for the else clause.
10.957 +
10.958 + self.namespace = body_namespace
10.959 + if is_module:
10.960 + self.module.namespace = self.namespace
10.961 +
10.962 + # Empty the raised exceptions for the else clause.
10.963 +
10.964 + self.namespace.raises = set()
10.965 + self.dispatches(try_.else_)
10.966 + self.namespace.raises = raises
10.967 +
10.968 + # Merge the namespaces.
10.969 +
10.970 + self.namespace = Namespace()
10.971 + if is_module:
10.972 + self.module.namespace = self.namespace
10.973 + self.namespace.merge_namespace(body_namespace)
10.974 + self.namespace.merge_namespace(handler_namespace)
10.975 +
10.976 + # Process the finally clause, if any.
10.977 +
10.978 + self.dispatches(try_.finally_)
10.979 +
10.980 + def visitYield(self, yield_):
10.981 + raise NotImplementedError, "The yield statement is not currently supported."
10.982 +
10.983 + # Utility methods.
10.984 +
10.985 + def get_builtin_instances(self, node, name):
10.986 + return set([Attribute(None, self.new_instance(node, attr.type)) for attr in self.builtins.namespace[name]])
10.987 +
10.988 + def new_instance(self, node, type):
10.989 +
10.990 + "For the given 'node', obtain an instance from the given 'type'."
10.991 +
10.992 + if not type.has_instance(node):
10.993 + instance = Instance()
10.994 + instance.namespace = Namespace()
10.995 + instance.namespace.store("__class__", set([Attribute(None, type)]))
10.996 + type.add_instance(node, instance)
10.997 + else:
10.998 + instance = type.get_instance(node)
10.999 +
10.1000 + return instance
10.1001 +
10.1002 + def invoke_subprogram(self, invoke, attribute):
10.1003 +
10.1004 + """
10.1005 + Invoke using the given 'invoke' node the subprogram represented by the
10.1006 + given 'attribute'.
10.1007 + """
10.1008 +
10.1009 + # Test for context information, making it into a real attribute.
10.1010 +
10.1011 + if attribute.context is not None:
10.1012 + context = Attribute(None, attribute.context)
10.1013 + target = attribute.type
10.1014 + else:
10.1015 + context = None
10.1016 + target = attribute.type
10.1017 +
10.1018 + # Test to see if anything has changed.
10.1019 +
10.1020 + if hasattr(invoke, "syscount") and invoke.syscount.has_key(target) and invoke.syscount[target] == self.system.count:
10.1021 + return
10.1022 +
10.1023 + # Remember the state of the system.
10.1024 +
10.1025 + else:
10.1026 + if not hasattr(invoke, "syscount"):
10.1027 + invoke.syscount = {}
10.1028 + invoke.syscount[target] = self.system.count
10.1029 +
10.1030 + # Provide the correct namespace for the invocation.
10.1031 + # This may be a "shared" namespace...
10.1032 +
10.1033 + if getattr(invoke, "share_locals", 0):
10.1034 + namespace = Namespace()
10.1035 + namespace.merge_namespace(self.namespace, everything=0)
10.1036 + using_module_namespace = self.namespace is self.module.namespace
10.1037 +
10.1038 + # Or it may be a structure...
10.1039 +
10.1040 + elif getattr(target, "structure", None):
10.1041 + namespace = Namespace()
10.1042 + using_module_namespace = 0
10.1043 +
10.1044 + # Or it may be a new namespace populated with the supplied parameters.
10.1045 +
10.1046 + else:
10.1047 + items = self.make_items(invoke, target, context)
10.1048 + namespace = Namespace()
10.1049 + namespace.merge_items(items)
10.1050 + using_module_namespace = 0
10.1051 +
10.1052 + # NOTE: Avoid PEP 227 (nested scopes) whilst permitting references to a
10.1053 + # NOTE: subprogram within itself. Do not define the name of the function
10.1054 + # NOTE: within a method definition.
10.1055 +
10.1056 + if getattr(target, "name", None) is not None and not getattr(target, "is_method", 0):
10.1057 + namespace.store(target.name, set([Attribute(None, target)]))
10.1058 +
10.1059 + # Process the subprogram.
10.1060 +
10.1061 + self.process_node(target, namespace, using_module_namespace)
10.1062 +
10.1063 + # NOTE: Improve and verify this.
10.1064 + # If the invocation returns a value, acquire the return types.
10.1065 +
10.1066 + if getattr(target, "returns_value", 0):
10.1067 + self.namespace.set_types(target.returns)
10.1068 + self.annotate(invoke)
10.1069 +
10.1070 + # If it is a normal block, merge the locals.
10.1071 + # This can happen in addition to the above because for things like
10.1072 + # logical expressions, the namespace can be modified whilst values are
10.1073 + # returned as results.
10.1074 +
10.1075 + if getattr(invoke, "share_locals", 0):
10.1076 + self.namespace.reset()
10.1077 +
10.1078 + # Merge the locals snapshots.
10.1079 +
10.1080 + for locals in target.return_locals:
10.1081 +
10.1082 + # For blocks returning values (such as operations), do not merge
10.1083 + # snapshots or results.
10.1084 +
10.1085 + if getattr(target, "returns_value", 0):
10.1086 + self.namespace.merge_namespace(locals, everything=0)
10.1087 +
10.1088 + # For blocks not returning values (such as loops), merge
10.1089 + # snapshots and results since they contain details of genuine
10.1090 + # returns.
10.1091 +
10.1092 + else:
10.1093 + self.namespace.merge_namespace(locals)
10.1094 +
10.1095 + # Incorporate any raised exceptions.
10.1096 +
10.1097 + if not hasattr(invoke, "raises"):
10.1098 + invoke.raises = set()
10.1099 + invoke.raises.update(target.raises)
10.1100 + self.namespace.raises.update(target.raises)
10.1101 +
10.1102 + def process_args(self, invocation):
10.1103 +
10.1104 + """
10.1105 + Process the arguments associated with an 'invocation'. Return whether
10.1106 + any arguments were processed.
10.1107 + """
10.1108 +
10.1109 + self.dispatches(invocation.pos_args)
10.1110 + self.dispatch_dict(invocation.kw_args)
10.1111 +
10.1112 + # Get type information for star and dstar arguments.
10.1113 +
10.1114 + if invocation.star is not None:
10.1115 + param, default = invocation.star
10.1116 + self.dispatch(default)
10.1117 + invocation.star = param, default
10.1118 +
10.1119 + if invocation.dstar is not None:
10.1120 + param, default = invocation.dstar
10.1121 + self.dispatch(default)
10.1122 + invocation.dstar = param, default
10.1123 +
10.1124 + if invocation.pos_args or invocation.kw_args or invocation.star or invocation.dstar:
10.1125 + return 1
10.1126 + else:
10.1127 + return 0
10.1128 +
10.1129 + def make_items(self, invocation, subprogram, context):
10.1130 +
10.1131 + """
10.1132 + Make an items mapping for the 'invocation' of the 'subprogram' using the
10.1133 + given 'context' (which may be None).
10.1134 + """
10.1135 +
10.1136 + # NOTE: Support class methods!
10.1137 +
10.1138 + if context is not None and isinstance(context.type, Instance):
10.1139 + pos_args = [Self(context)] + invocation.pos_args
10.1140 + else:
10.1141 + pos_args = invocation.pos_args
10.1142 +
10.1143 + # Duplicate the keyword arguments - we remove them in processing below.
10.1144 +
10.1145 + kw_args = {}
10.1146 + kw_args.update(invocation.kw_args)
10.1147 +
10.1148 + # Sort the arguments into positional and keyword arguments.
10.1149 +
10.1150 + params = subprogram.params
10.1151 + items = []
10.1152 + star_args = []
10.1153 +
10.1154 + # Match each positional argument, taking excess arguments as star args.
10.1155 +
10.1156 + for arg in pos_args:
10.1157 + if params:
10.1158 + param, default = params[0]
10.1159 + if arg is None:
10.1160 + arg = default
10.1161 + if hasattr(arg, "types"):
10.1162 + items.append((param, arg.types))
10.1163 + else:
10.1164 + items.append((param, set())) # Annotation has not succeeded.
10.1165 + params = params[1:]
10.1166 + else:
10.1167 + star_args.append(arg)
10.1168 +
10.1169 + # Collect the remaining defaults.
10.1170 +
10.1171 + while params:
10.1172 + param, default = params[0]
10.1173 + if kw_args.has_key(param):
10.1174 + arg = kw_args[param]
10.1175 + del kw_args[param]
10.1176 + elif default is not None:
10.1177 + self.dispatch(default)
10.1178 + arg = default
10.1179 + else:
10.1180 + raise AnnotationMessage, "No argument supplied in '%s' for parameter '%s'." % (subprogram, param)
10.1181 + if hasattr(arg, "types"):
10.1182 + items.append((param, arg.types))
10.1183 + else:
10.1184 + items.append((param, set())) # Annotation has not succeeded.
10.1185 + params = params[1:]
10.1186 +
10.1187 + dstar_args = kw_args.items()
10.1188 +
10.1189 + # Construct temporary objects.
10.1190 +
10.1191 + if star_args:
10.1192 + star_invocation = self.make_star_args(invocation, subprogram, star_args)
10.1193 + self.dispatch(star_invocation)
10.1194 + star_types = star_invocation.types
10.1195 + else:
10.1196 + star_types = None
10.1197 +
10.1198 + if dstar_args:
10.1199 + dstar_invocation = self.make_dstar_args(invocation, subprogram, dstar_args)
10.1200 + self.dispatch(dstar_invocation)
10.1201 + dstar_types = dstar_invocation.types
10.1202 + else:
10.1203 + dstar_types = None
10.1204 +
10.1205 + # NOTE: Merge the objects properly.
10.1206 +
10.1207 + star_types = star_types or invocation.star and invocation.star.types
10.1208 + dstar_types = dstar_types or invocation.dstar and invocation.dstar.types
10.1209 +
10.1210 + # Add star and dstar.
10.1211 +
10.1212 + if star_types is not None:
10.1213 + if subprogram.star is not None:
10.1214 + param, default = subprogram.star
10.1215 + items.append((param, star_types))
10.1216 + else:
10.1217 + raise AnnotationMessage, "Invocation provides unwanted *args."
10.1218 + elif subprogram.star is not None:
10.1219 + param, default = subprogram.star
10.1220 + if not hasattr(default, "types"):
10.1221 + subprogram.star = param, self.dispatch(default) # NOTE: Review reprocessing.
10.1222 + items.append((param, default.types))
10.1223 +
10.1224 + if dstar_types is not None:
10.1225 + if subprogram.dstar is not None:
10.1226 + param, default = subprogram.dstar
10.1227 + items.append((param, dstar_types))
10.1228 + else:
10.1229 + raise AnnotationMessage, "Invocation provides unwanted **args."
10.1230 + elif subprogram.dstar is not None:
10.1231 + param, default = subprogram.dstar
10.1232 + if not hasattr(default, "types"):
10.1233 + subprogram.dstar = param, self.dispatch(default) # NOTE: Review reprocessing.
10.1234 + items.append((param, default.types))
10.1235 +
10.1236 + # Record the parameter types.
10.1237 +
10.1238 + self.annotate_parameters(subprogram, items)
10.1239 + return subprogram.paramtypes.items()
10.1240 +
10.1241 + def make_star_args(self, invocation, subprogram, star_args):
10.1242 +
10.1243 + "Make a subprogram which initialises a list containing 'star_args'."
10.1244 +
10.1245 + if not hasattr(invocation, "stars"):
10.1246 + invocation.stars = {}
10.1247 +
10.1248 + if not invocation.stars.has_key(subprogram.full_name()):
10.1249 + instance = getattr(invocation, "instance", None)
10.1250 +
10.1251 + code = [
10.1252 + Return(
10.1253 + instance=instance,
10.1254 + expr=MakeTuple(
10.1255 + instance=instance,
10.1256 + nodes=star_args
10.1257 + )
10.1258 + )
10.1259 + ]
10.1260 +
10.1261 + new_subprogram = Subprogram(
10.1262 + instance=instance,
10.1263 + name=None,
10.1264 + returns_value=1,
10.1265 + params=[],
10.1266 + star=None,
10.1267 + dstar=None,
10.1268 + code=code
10.1269 + )
10.1270 +
10.1271 + subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
10.1272 +
10.1273 + invocation.stars[subprogram.full_name()] = InvokeRef(
10.1274 + invocation.original,
10.1275 + instance=instance,
10.1276 + produces_result=1,
10.1277 + ref=new_subprogram
10.1278 + )
10.1279 +
10.1280 + return invocation.stars[subprogram.full_name()]
10.1281 +
10.1282 + def make_dstar_args(self, invocation, subprogram, dstar_args):
10.1283 +
10.1284 + """
10.1285 + Make a subprogram which initialises a dictionary built from the given
10.1286 + 'dstar_args'.
10.1287 + """
10.1288 +
10.1289 + if not hasattr(invocation, "dstars"):
10.1290 + invocation.dstars = {}
10.1291 +
10.1292 + if not invocation.dstars.has_key(subprogram.full_name()):
10.1293 + instance = getattr(invocation, "instance", None)
10.1294 +
10.1295 + code=[
10.1296 + StoreTemp(
10.1297 + instance=instance,
10.1298 + expr=InvokeFunction(
10.1299 + invocation.original,
10.1300 + instance=instance,
10.1301 + expr=LoadAttr(
10.1302 + instance=instance,
10.1303 + expr=LoadRef(
10.1304 + instance=instance,
10.1305 + ref=self.builtins
10.1306 + ),
10.1307 + name="dict",
10.1308 + nstype="module",
10.1309 + )
10.1310 + )
10.1311 + )
10.1312 + ]
10.1313 +
10.1314 + for arg, value in dstar_args:
10.1315 +
10.1316 + # NOTE: Constant not added to table.
10.1317 +
10.1318 + constant = Constant(name=repr(arg), value=arg)
10.1319 + code += [
10.1320 + StoreTemp(
10.1321 + instance=instance,
10.1322 + expr=InvokeFunction(
10.1323 + instance=instance,
10.1324 + expr=LoadName(
10.1325 + instance=instance,
10.1326 + name=constant.typename
10.1327 + )
10.1328 + ),
10.1329 + index="const"
10.1330 + ),
10.1331 + InvokeFunction(
10.1332 + invocation.original,
10.1333 + instance=instance,
10.1334 + expr=LoadAttr(
10.1335 + instance=instance,
10.1336 + expr=LoadTemp(
10.1337 + instance=instance
10.1338 + ),
10.1339 + name="__setitem__"
10.1340 + ),
10.1341 + args=[
10.1342 + LoadTemp(
10.1343 + instance=instance,
10.1344 + index="const",
10.1345 + release=1
10.1346 + ),
10.1347 + value
10.1348 + ]
10.1349 + )
10.1350 + ]
10.1351 +
10.1352 + code += [
10.1353 + Return(
10.1354 + instance=instance,
10.1355 + expr=LoadTemp(
10.1356 + instance=instance,
10.1357 + release=1
10.1358 + )
10.1359 + )
10.1360 + ]
10.1361 +
10.1362 + new_subprogram = Subprogram(
10.1363 + instance=instance,
10.1364 + name=None,
10.1365 + returns_value=1,
10.1366 + params=[],
10.1367 + star=None,
10.1368 + dstar=None,
10.1369 + code=code
10.1370 + )
10.1371 + subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
10.1372 +
10.1373 + invocation.dstars[subprogram.full_name()] = InvokeRef(
10.1374 + invocation.original,
10.1375 + instance=instance,
10.1376 + produces_result=1,
10.1377 + ref=new_subprogram
10.1378 + )
10.1379 +
10.1380 + return invocation.dstars[subprogram.full_name()]
10.1381 +
10.1382 +# Namespace-related abstractions.
10.1383 +
10.1384 +class Namespace:
10.1385 +
10.1386 + """
10.1387 + A local namespace which may either relate to a genuine set of function
10.1388 + locals or the initialisation of a structure or module.
10.1389 + """
10.1390 +
10.1391 + def __init__(self):
10.1392 +
10.1393 + """
10.1394 + Initialise the namespace with a mapping of local names to possible
10.1395 + types, a list of return values and of possible returned local
10.1396 + namespaces. The namespace also tracks the "current" types and a mapping
10.1397 + of temporary value names to types.
10.1398 + """
10.1399 +
10.1400 + self.names = {}
10.1401 + self.returns = set()
10.1402 + self.return_locals = set()
10.1403 + self.raises = set()
10.1404 + self.temp = {}
10.1405 + self.types = set()
10.1406 +
10.1407 + def set_types(self, types):
10.1408 +
10.1409 + "Set the current collection of 'types'."
10.1410 +
10.1411 + self.types = types.copy()
10.1412 +
10.1413 + def add(self, name, types):
10.1414 +
10.1415 + "Add to the entry with the given 'name' the specified 'types'."
10.1416 +
10.1417 + if self.names.has_key(name):
10.1418 + self.names[name].update(types)
10.1419 + else:
10.1420 + self.store(name, types)
10.1421 +
10.1422 + def store(self, name, types):
10.1423 +
10.1424 + "Store in (or associate with) the given 'name' the specified 'types'."
10.1425 +
10.1426 + self.names[name] = types.copy()
10.1427 +
10.1428 + __setitem__ = store
10.1429 +
10.1430 + def load(self, name):
10.1431 +
10.1432 + "Load the types associated with the given 'name'."
10.1433 +
10.1434 + return self.names[name]
10.1435 +
10.1436 + __getitem__ = load
10.1437 +
10.1438 + def has_key(self, name):
10.1439 + return self.names.has_key(name)
10.1440 +
10.1441 + def revoke(self, name, type):
10.1442 +
10.1443 + "Revoke from the entry for the given 'name' the specified 'type'."
10.1444 +
10.1445 + new_types = self.names[name].copy()
10.1446 + new_types.remove(type)
10.1447 + self.names[name] = new_types
10.1448 +
10.1449 + def revoke_exception_type(self, type):
10.1450 +
10.1451 + "Revoke the given 'type' from the collection of exception types."
10.1452 +
10.1453 + self.raises.remove(type)
10.1454 +
10.1455 + def revoke_temp_type(self, index, type):
10.1456 +
10.1457 + "Revoke from the temporary variable 'index' the given 'type'."
10.1458 +
10.1459 + new_types = self.temp[index][-1].copy()
10.1460 + new_types.remove(type)
10.1461 + self.temp[index][-1] = new_types
10.1462 +
10.1463 + def merge_namespace(self, namespace, everything=1):
10.1464 +
10.1465 + """
10.1466 + Merge items from the given 'namespace' with this namespace. When the
10.1467 + optional 'everything' parameter is set to a false value (unlike the
10.1468 + default), return values and locals snapshots will not be copied to this
10.1469 + namespace.
10.1470 + """
10.1471 +
10.1472 + self.merge_items(namespace.names.items())
10.1473 + self.raises.update(namespace.raises)
10.1474 + if everything:
10.1475 + self.returns.update(namespace.returns)
10.1476 + self.return_locals.update(namespace.return_locals)
10.1477 + for name, values in namespace.temp.items():
10.1478 + if values:
10.1479 + if not self.temp.has_key(name) or not self.temp[name]:
10.1480 + self.temp[name] = [set()]
10.1481 + self.temp[name][-1].update(values[-1])
10.1482 +
10.1483 + def merge_items(self, items):
10.1484 +
10.1485 + "Merge the given 'items' with this namespace."
10.1486 +
10.1487 + for name, types in items:
10.1488 + self.merge(name, types)
10.1489 +
10.1490 + def merge(self, name, types):
10.1491 +
10.1492 + "Merge the entry for the given 'name' and 'types' with this namespace."
10.1493 +
10.1494 + if not self.names.has_key(name):
10.1495 + self.names[name] = types.copy()
10.1496 + else:
10.1497 + existing = self.names[name]
10.1498 + existing.update(types)
10.1499 +
10.1500 + def snapshot(self):
10.1501 +
10.1502 + "Make a snapshot of the locals and remember them."
10.1503 +
10.1504 + namespace = Namespace()
10.1505 + namespace.merge_namespace(self)
10.1506 + self.return_locals.add(namespace)
10.1507 +
10.1508 + def reset(self):
10.1509 +
10.1510 + "Reset a namespace in preparation for merging with returned locals."
10.1511 +
10.1512 + self.names = {}
10.1513 +
10.1514 + def __repr__(self):
10.1515 + return repr(self.names) + " (temp) " + repr(self.temp)
10.1516 +
10.1517 +class Importer:
10.1518 +
10.1519 + "An import machine, searching for and loading modules."
10.1520 +
10.1521 + def __init__(self, path=None):
10.1522 +
10.1523 + """
10.1524 + Initialise the importer with the given search 'path' - a list of
10.1525 + directories to search for Python modules.
10.1526 + """
10.1527 +
10.1528 + self.path = path or [os.getcwd()]
10.1529 + self.path.append(libdir)
10.1530 + self.modules = {}
10.1531 +
10.1532 + def find_in_path(self, name):
10.1533 +
10.1534 + """
10.1535 + Find the given module 'name' in the search path, returning None where no
10.1536 + such module could be found, or a 2-tuple from the 'find' method
10.1537 + otherwise.
10.1538 + """
10.1539 +
10.1540 + for d in self.path:
10.1541 + m = self.find(d, name)
10.1542 + if m: return m
10.1543 + return None
10.1544 +
10.1545 + def find(self, d, name):
10.1546 +
10.1547 + """
10.1548 + In the directory 'd', find the given module 'name', where 'name' can
10.1549 + either refer to a single file module or to a package. Return None if the
10.1550 + 'name' cannot be associated with either a file or a package directory,
10.1551 + or a 2-tuple from '_find_package' or '_find_module' otherwise.
10.1552 + """
10.1553 +
10.1554 + m = self._find_package(d, name)
10.1555 + if m: return m
10.1556 + m = self._find_module(d, name)
10.1557 + if m: return m
10.1558 + return None
10.1559 +
10.1560 + def _find_module(self, d, name):
10.1561 +
10.1562 + """
10.1563 + In the directory 'd', find the given module 'name', returning None where
10.1564 + no suitable file exists in the directory, or a 2-tuple consisting of
10.1565 + None (indicating that no package directory is involved) and a filename
10.1566 + indicating the location of the module.
10.1567 + """
10.1568 +
10.1569 + name_py = name + os.extsep + "py"
10.1570 + filename = self._find_file(d, name_py)
10.1571 + if filename:
10.1572 + return None, filename
10.1573 + return None
10.1574 +
10.1575 + def _find_package(self, d, name):
10.1576 +
10.1577 + """
10.1578 + In the directory 'd', find the given package 'name', returning None
10.1579 + where no suitable package directory exists, or a 2-tuple consisting of
10.1580 + a directory (indicating the location of the package directory itself)
10.1581 + and a filename indicating the location of the __init__.py module which
10.1582 + declares the package's top-level contents.
10.1583 + """
10.1584 +
10.1585 + filename = self._find_file(d, name)
10.1586 + if filename:
10.1587 + init_py = "__init__" + os.path.extsep + "py"
10.1588 + init_py_filename = self._find_file(filename, init_py)
10.1589 + if init_py_filename:
10.1590 + return filename, init_py_filename
10.1591 + return None
10.1592 +
10.1593 + def _find_file(self, d, filename):
10.1594 +
10.1595 + """
10.1596 + Return the filename obtained when searching the directory 'd' for the
10.1597 + given 'filename', or None if no actual file exists for the filename.
10.1598 + """
10.1599 +
10.1600 + filename = os.path.join(d, filename)
10.1601 + if os.path.exists(filename):
10.1602 + return filename
10.1603 + else:
10.1604 + return None
10.1605 +
10.1606 + def load(self, name, builtins, alias=None):
10.1607 +
10.1608 + """
10.1609 + Load the module or package with the given 'name' and using the specified
10.1610 + 'builtins'. Return an Attribute object referencing the loaded module or
10.1611 + package, or None if no such module or package exists.
10.1612 + """
10.1613 +
10.1614 + if self.modules.has_key(name):
10.1615 + return Attribute(None, self.modules[name])
10.1616 +
10.1617 + path = name.split(".")
10.1618 + m = self.find_in_path(path[0])
10.1619 + if not m:
10.1620 + return None # NOTE: Import error.
10.1621 + d, filename = m
10.1622 +
10.1623 + if self.modules.has_key(path[0]):
10.1624 + top = module = self.modules[path[0]]
10.1625 + else:
10.1626 + top = module = self.modules[path[0]] = load(filename, builtins, path[0], self, no_annotate=1)
10.1627 + annotate(module, builtins, self)
10.1628 +
10.1629 + if len(path) > 1:
10.1630 + path_so_far = path[:1]
10.1631 + for p in path[1:]:
10.1632 + path_so_far.append(p)
10.1633 + m = self.find(d, p)
10.1634 + if not m:
10.1635 + return None # NOTE: Import error.
10.1636 + d, filename = m
10.1637 + module_name = ".".join(path_so_far)
10.1638 +
10.1639 + if self.modules.has_key(module_name):
10.1640 + submodule = self.modules[module_name]
10.1641 + else:
10.1642 + submodule = self.modules[module_name] = load(filename, builtins, module_name, self, no_annotate=1)
10.1643 + annotate(submodule, builtins, self)
10.1644 +
10.1645 + # Store the submodule within its parent module.
10.1646 +
10.1647 + module.namespace[p] = [Attribute(None, submodule)]
10.1648 + module = submodule
10.1649 +
10.1650 + if alias:
10.1651 + return Attribute(None, module)
10.1652 + else:
10.1653 + return Attribute(None, top)
10.1654 +
10.1655 +def combine(target, additions):
10.1656 +
10.1657 + """
10.1658 + Merge into the 'target' sequence the given 'additions', preventing duplicate
10.1659 + items.
10.1660 + """
10.1661 +
10.1662 + for addition in additions:
10.1663 + if addition not in target:
10.1664 + target.append(addition)
10.1665 +
10.1666 +def find_attributes(structure, name):
10.1667 +
10.1668 + """
10.1669 + Find for the given 'structure' all attributes for the given 'name', visiting
10.1670 + base classes where appropriate and returning the attributes in order of
10.1671 + descending precedence for all possible base classes.
10.1672 +
10.1673 + The elements in the result list are 2-tuples which contain the attribute and
10.1674 + the structure involved in accessing the attribute.
10.1675 + """
10.1676 +
10.1677 + # First attempt to search the instance/class namespace.
10.1678 +
10.1679 + try:
10.1680 + l = structure.namespace.load(name)
10.1681 + attributes = []
10.1682 + for attribute in l:
10.1683 + attributes.append((attribute, structure))
10.1684 +
10.1685 + # If that does not work, attempt to investigate any class or base classes.
10.1686 +
10.1687 + except KeyError:
10.1688 + attributes = []
10.1689 +
10.1690 + # Investigate any instance's implementing class.
10.1691 +
10.1692 + if isinstance(structure, Instance):
10.1693 + for attr in structure.namespace.load("__class__"):
10.1694 + cls = attr.type
10.1695 + l = get_attributes(cls, name)
10.1696 + combine(attributes, l)
10.1697 +
10.1698 + # Investigate any class's base classes.
10.1699 +
10.1700 + elif isinstance(structure, Class):
10.1701 +
10.1702 + # If no base classes exist, return an indicator that no attribute
10.1703 + # exists.
10.1704 +
10.1705 + if not structure.base_refs:
10.1706 + return [(None, structure)]
10.1707 +
10.1708 + # Otherwise, find all possible base classes.
10.1709 +
10.1710 + for base_refs in structure.base_refs:
10.1711 + base_attributes = []
10.1712 +
10.1713 + # For each base class, find attributes either in the base
10.1714 + # class or its own base classes.
10.1715 +
10.1716 + for base_ref in base_refs:
10.1717 + l = get_attributes(base_ref, name)
10.1718 + combine(base_attributes, l)
10.1719 +
10.1720 + combine(attributes, base_attributes)
10.1721 +
10.1722 + return attributes
10.1723 +
10.1724 +def get_attributes(structure, name):
10.1725 +
10.1726 + """
10.1727 + Return all possible attributes for the given 'structure' having the given
10.1728 + 'name', wrapping each attribute in an Attribute object which includes
10.1729 + context information for the attribute access.
10.1730 +
10.1731 + The elements in the result list are 2-tuples which contain the attribute and
10.1732 + the structure involved in accessing the attribute.
10.1733 + """
10.1734 +
10.1735 + if isinstance(structure, Attribute):
10.1736 + structure = structure.type
10.1737 + results = []
10.1738 + for attribute, accessor in find_attributes(structure, name):
10.1739 +
10.1740 + # Detect class attribute access via instances.
10.1741 +
10.1742 + if attribute is not None and isinstance(structure, Instance) and isinstance(accessor, Class):
10.1743 + attribute = accessor.get_attribute_for_instance(attribute, structure)
10.1744 +
10.1745 + # Produce an attribute with the appropriate context.
10.1746 +
10.1747 + if attribute is not None and isinstance(structure, Structure):
10.1748 + results.append((Attribute(structure, attribute.type), accessor))
10.1749 + else:
10.1750 + results.append((attribute, accessor))
10.1751 +
10.1752 + return results
10.1753 +
10.1754 +def prompt(vars):
10.1755 + try:
10.1756 + while 1:
10.1757 + s = raw_input("> ")
10.1758 + print eval(s, vars)
10.1759 + except EOFError:
10.1760 + pass
10.1761 +
10.1762 +# Convenience functions.
10.1763 +
10.1764 +def load(name, builtins=None, module_name=None, importer=None, no_annotate=0):
10.1765 +
10.1766 + """
10.1767 + Load the module with the given 'name' (which may be a full module path),
10.1768 + using the optional 'builtins' to resolve built-in names, and using the
10.1769 + optional 'importer' to provide a means of finding and loading modules.
10.1770 + """
10.1771 +
10.1772 + module = simplify.simplify(name, builtins is None, module_name)
10.1773 + simplify.fixnames.fix(module, builtins)
10.1774 + if not no_annotate:
10.1775 + annotate(module, builtins, importer)
10.1776 + return module
10.1777 +
10.1778 +def annotate(module, builtins=None, importer=None):
10.1779 +
10.1780 + """
10.1781 + Annotate the given 'module', also employing the optional 'builtins' module,
10.1782 + if specified. If the optional 'importer' is given, use that to find and load
10.1783 + modules.
10.1784 + """
10.1785 +
10.1786 + annotator = Annotator(importer)
10.1787 + if builtins is not None:
10.1788 + annotator.process(module, builtins)
10.1789 + else:
10.1790 + annotator.process(module)
10.1791 +
10.1792 +# vim: tabstop=4 expandtab shiftwidth=4
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/simplify/fixnames.py Sun May 27 18:25:25 2007 +0200
11.3 @@ -0,0 +1,489 @@
11.4 +#!/usr/bin/env python
11.5 +
11.6 +"""
11.7 +Fix name-related operations. The code in this module operates upon simplified
11.8 +program node trees.
11.9 +
11.10 +Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk>
11.11 +
11.12 +This software is free software; you can redistribute it and/or
11.13 +modify it under the terms of the GNU General Public License as
11.14 +published by the Free Software Foundation; either version 2 of
11.15 +the License, or (at your option) any later version.
11.16 +
11.17 +This software is distributed in the hope that it will be useful,
11.18 +but WITHOUT ANY WARRANTY; without even the implied warranty of
11.19 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11.20 +GNU General Public License for more details.
11.21 +
11.22 +You should have received a copy of the GNU General Public
11.23 +License along with this library; see the file LICENCE.txt
11.24 +If not, write to the Free Software Foundation, Inc.,
11.25 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
11.26 +
11.27 +--------
11.28 +
11.29 +To use this module, the easiest approach is to use the fix function:
11.30 +
11.31 +fix(module)
11.32 +
11.33 +The more complicated approach involves instantiating a Fixer object:
11.34 +
11.35 +fixer = Fixer()
11.36 +
11.37 +Then, applying the fixer to an existing module:
11.38 +
11.39 +fixer.process(module)
11.40 +
11.41 +If a module containing built-in classes and functions exists, apply the fixer as
11.42 +follows:
11.43 +
11.44 +fixer.process(module, builtins)
11.45 +"""
11.46 +
11.47 +from simplify.simplified import *
11.48 +
11.49 +# Fixing of name-related operations.
11.50 +
11.51 +class Fixer(Visitor):
11.52 +
11.53 + """
11.54 + The name fixer which traverses the program nodes in a module, typically
11.55 + depth-first, and maintains a record of name usage in the different
11.56 + namespaces. As a consequence of various observations, some parts of the
11.57 + program node tree are modified with different operations employed to those
11.58 + originally defined.
11.59 +
11.60 + There are two kinds of subprograms in modules: functions/methods and
11.61 + internal subprograms which support things like loops. The latter kind of
11.62 + subprogram may acquire the locals from their callers and must therefore be
11.63 + traversed with information from such callers. Thus, we choose the top-level
11.64 + code and all functions/methods as roots for processing, following
11.65 + invocations of internal subprograms in order to reach all subprograms that
11.66 + are defined in each module.
11.67 +
11.68 + top-level
11.69 + ...
11.70 + invoke function
11.71 + ...
11.72 + invoke loop -> subprogram (internal)
11.73 + ...
11.74 +
11.75 + subprogram (function)
11.76 + ...
11.77 + invoke loop -> subprogram (internal)
11.78 + ...
11.79 +
11.80 + ...
11.81 +
11.82 + The above approach should guarantee that all subprograms are traversed and
11.83 + that all name lookups are correctly categorised.
11.84 + """
11.85 +
11.86 + def __init__(self):
11.87 +
11.88 + "Initialise the name fixer."
11.89 +
11.90 + Visitor.__init__(self)
11.91 +
11.92 + # Satisfy visitor issues.
11.93 +
11.94 + self.visitor = self
11.95 +
11.96 + def process(self, module, builtins=None):
11.97 +
11.98 + """
11.99 + Process the given 'module' optionally using some 'builtins' to reference
11.100 + built-in objects.
11.101 + """
11.102 +
11.103 + # The fixer maintains a list of transformed subprograms (added for each
11.104 + # of the processing "roots" and also for each invoked internal
11.105 + # subprogram), along with a list of current subprograms (used to avoid
11.106 + # recursion issues) and a list of current namespaces (used to recall
11.107 + # namespaces upon invoking internal subprograms).
11.108 +
11.109 + self.subprograms = []
11.110 + self.current_subprograms = []
11.111 + self.current_namespaces = []
11.112 +
11.113 + # First, process the top-level code, finding out which names are
11.114 + # defined at that level.
11.115 +
11.116 + self.global_namespace = None
11.117 + self.module = module
11.118 + self.builtins = builtins or module
11.119 +
11.120 + self.process_node(self.module)
11.121 +
11.122 + # Then, process all functions and methods, providing a global namespace.
11.123 + # By setting a global namespace, we influence the resolution of names:
11.124 + # those which are global to the top-level module (processed above) are
11.125 + # considered as built-in names, whereas those which are global to a
11.126 + # function or method are searched for in the global namespace.
11.127 +
11.128 + self.global_namespace = self.namespace
11.129 +
11.130 + for subprogram in self.module.simplifier.subprograms:
11.131 +
11.132 + # Internal subprograms are skipped here and processed specially via
11.133 + # Invoke nodes.
11.134 +
11.135 + if not getattr(subprogram, "internal", 0):
11.136 + self.subprograms.append(self.process_node(subprogram))
11.137 +
11.138 + # Ultimately, we redefine the list of subprograms on the visitor.
11.139 +
11.140 + self.module.simplifier.subprograms = self.subprograms
11.141 + return self.module
11.142 +
11.143 + def process_node(self, node, namespace=None):
11.144 +
11.145 + """
11.146 + Process a subprogram or module 'node', discovering from attributes on
11.147 + 'node' any initial locals. Return a modified subprogram or module.
11.148 + """
11.149 +
11.150 + # Do not process subprograms already being processed.
11.151 +
11.152 + if node in self.current_subprograms:
11.153 + return None
11.154 +
11.155 + # Obtain a namespace either based on locals or on a structure.
11.156 +
11.157 + structure = structure=getattr(node, "structure", None)
11.158 +
11.159 + # If passed some namespace, use that as the current namespace.
11.160 +
11.161 + if namespace is not None:
11.162 + self.namespace.merge_namespace(namespace)
11.163 + else:
11.164 + self.namespace = NameOrganiser(structure)
11.165 +
11.166 + # Record the current subprogram and namespace.
11.167 +
11.168 + self.current_subprograms.append(node)
11.169 + self.current_namespaces.append(self.namespace)
11.170 +
11.171 + # NOTE: Avoid PEP 227 (nested scopes) whilst permitting references to a
11.172 + # NOTE: subprogram within itself. Do not define the name of the function
11.173 + # NOTE: within a method definition.
11.174 +
11.175 + if isinstance(node, Subprogram) and getattr(node, "name", None) is not None and not getattr(node, "is_method", 0):
11.176 + self.namespace.store(node.name)
11.177 +
11.178 + # Register the names of parameters in the namespace.
11.179 +
11.180 + if hasattr(node, "params"):
11.181 + new_params = []
11.182 + for param, default in node.params:
11.183 + new_params.append((param, self.dispatch(default)))
11.184 + self.namespace.store(param)
11.185 + node.params = new_params
11.186 + if getattr(node, "star", None):
11.187 + param, default = node.star
11.188 + self.namespace.store(param)
11.189 + node.star = param, self.dispatch(default)
11.190 + if getattr(node, "dstar", None):
11.191 + param, default = node.dstar
11.192 + self.namespace.store(param)
11.193 + node.dstar = param, self.dispatch(default)
11.194 +
11.195 + # Add namespace details to any structure involved.
11.196 +
11.197 + if hasattr(node, "structure") and node.structure is not None:
11.198 +
11.199 + # Initialise bases where appropriate.
11.200 +
11.201 + if hasattr(node.structure, "bases"):
11.202 + bases = []
11.203 + for base in node.structure.bases:
11.204 + bases.append(self.dispatch(base))
11.205 + node.structure.bases = bases
11.206 +
11.207 + # Dispatch to the code itself.
11.208 +
11.209 + result = self.dispatch(node)
11.210 + result.organiser = self.namespace
11.211 +
11.212 + # Restore the previous subprogram and namespace.
11.213 +
11.214 + self.current_namespaces.pop()
11.215 + if self.current_namespaces:
11.216 + self.namespace = self.current_namespaces[-1]
11.217 + self.current_subprograms.pop()
11.218 +
11.219 + return result
11.220 +
11.221 + # Visitor methods.
11.222 +
11.223 + def default(self, node):
11.224 +
11.225 + """
11.226 + Process the given 'node', given that it does not have a specific
11.227 + handler.
11.228 + """
11.229 +
11.230 + for attr in ("pos_args",):
11.231 + value = getattr(node, attr, None)
11.232 + if value is not None:
11.233 + setattr(node, attr, self.dispatches(value))
11.234 + for attr in ("kw_args",):
11.235 + value = getattr(node, attr, None)
11.236 + if value is not None:
11.237 + setattr(node, attr, self.dispatch_dict(value))
11.238 + for attr in ("expr", "lvalue", "test", "star", "dstar"):
11.239 + value = getattr(node, attr, None)
11.240 + if value is not None:
11.241 + setattr(node, attr, self.dispatch(value))
11.242 + for attr in ("body", "else_", "handler", "finally_", "code", "choices", "nodes"):
11.243 + value = getattr(node, attr, None)
11.244 + if value is not None:
11.245 + setattr(node, attr, self.dispatches(value))
11.246 + return node
11.247 +
11.248 + def dispatch(self, node, *args):
11.249 + return Visitor.dispatch(self, node, *args)
11.250 +
11.251 + def visitGlobal(self, global_):
11.252 + for name in global_.names:
11.253 + self.namespace.make_global(name)
11.254 + return global_
11.255 +
11.256 + def visitLoadName(self, loadname):
11.257 +
11.258 + "Transform the 'loadname' node to a specific, scope-sensitive node."
11.259 +
11.260 + scope = self.namespace.find_for_load(loadname.name)
11.261 +
11.262 + # For structure namespaces, load an attribute.
11.263 +
11.264 + if scope == "structure":
11.265 + result = self.dispatch(
11.266 + LoadAttr(loadname.original, loadname.defining,
11.267 + expr=LoadRef(loadname.original,
11.268 + ref=self.namespace.structure),
11.269 + name=loadname.name,
11.270 + nstype="structure")
11.271 + )
11.272 +
11.273 + # For global accesses (ie. those outside the local namespace)...
11.274 +
11.275 + elif scope == "global":
11.276 +
11.277 + # Where a distinct global namespace exists, examine it.
11.278 +
11.279 + if self.global_namespace is not None:
11.280 + scope = self.global_namespace.find_for_load(loadname.name)
11.281 +
11.282 + # Where the name is outside the global namespace, it must be a
11.283 + # built-in.
11.284 +
11.285 + if scope == "global":
11.286 + result = self.dispatch(
11.287 + LoadAttr(loadname.original, loadname.defining,
11.288 + expr=LoadRef(loadname.original,
11.289 + ref=self.builtins),
11.290 + name=loadname.name,
11.291 + nstype="module")
11.292 + )
11.293 +
11.294 + # Otherwise, it is within the global namespace and must be a
11.295 + # global.
11.296 +
11.297 + else:
11.298 + result = self.dispatch(
11.299 + LoadAttr(loadname.original, loadname.defining,
11.300 + expr=LoadRef(loadname.original,
11.301 + ref=self.module),
11.302 + name=loadname.name,
11.303 + nstype="module")
11.304 + )
11.305 +
11.306 + # Where no global namespace exists, we are at the module level and
11.307 + # must be accessing a built-in.
11.308 +
11.309 + else:
11.310 + result = self.dispatch(
11.311 + LoadAttr(loadname.original, loadname.defining,
11.312 + expr=LoadRef(loadname.original,
11.313 + ref=self.builtins),
11.314 + name=loadname.name,
11.315 + nstype="module")
11.316 + )
11.317 +
11.318 + # For local accesses...
11.319 +
11.320 + else:
11.321 +
11.322 + # Where a distinct global namespace exists, it must be a local.
11.323 +
11.324 + if self.global_namespace is not None:
11.325 + result = loadname
11.326 +
11.327 + # Otherwise, we must be accessing a global (which is local at the
11.328 + # module level).
11.329 +
11.330 + else:
11.331 + result = self.dispatch(
11.332 + LoadAttr(loadname.original, loadname.defining,
11.333 + expr=LoadRef(loadname.original,
11.334 + ref=self.module),
11.335 + name=loadname.name,
11.336 + nstype="module")
11.337 + )
11.338 +
11.339 + return result
11.340 +
11.341 + def visitStoreName(self, storename):
11.342 +
11.343 + "Transform the 'storename' node to a specific, scope-sensitive node."
11.344 +
11.345 + scope = self.namespace.find_for_store(storename.name)
11.346 +
11.347 + # For structure namespaces, store an attribute.
11.348 +
11.349 + if scope == "structure":
11.350 + self.namespace.store(storename.name)
11.351 +
11.352 + return self.dispatch(
11.353 + StoreAttr(storename.original, storename.defining,
11.354 + lvalue=LoadRef(storename.original,
11.355 + ref=self.namespace.structure),
11.356 + name=storename.name,
11.357 + expr=storename.expr,
11.358 + nstype="structure")
11.359 + )
11.360 +
11.361 + # Where the name is outside the local namespace, disallow any built-in
11.362 + # assignment and store the name globally.
11.363 +
11.364 + elif scope == "global":
11.365 + return self.dispatch(
11.366 + StoreAttr(storename.original, storename.defining,
11.367 + lvalue=LoadRef(storename.original,
11.368 + ref=self.module),
11.369 + name=storename.name,
11.370 + expr=storename.expr,
11.371 + nstype="module")
11.372 + )
11.373 +
11.374 + # For local namespace accesses...
11.375 +
11.376 + else:
11.377 + self.namespace.store(storename.name)
11.378 +
11.379 + # If a distinct global namespace exists, it must be a local access.
11.380 +
11.381 + if self.global_namespace is not None:
11.382 + return storename
11.383 +
11.384 + # Otherwise, the name is being set at the module level and is
11.385 + # considered global.
11.386 +
11.387 + else:
11.388 + return self.dispatch(
11.389 + StoreAttr(storename.original, storename.defining,
11.390 + lvalue=LoadRef(storename.original,
11.391 + ref=self.module),
11.392 + name=storename.name,
11.393 + expr=storename.expr,
11.394 + nstype="module")
11.395 + )
11.396 +
11.397 + def visitInvokeFunction(self, invoke):
11.398 +
11.399 + "Transform the 'invoke' node, performing processing on subprograms."
11.400 +
11.401 + return self.default(invoke)
11.402 +
11.403 + def visitInvokeRef(self, invoke):
11.404 +
11.405 + "Transform the 'invoke' node, performing processing on subprograms."
11.406 +
11.407 + # The special case of internal subprogram invocation is addressed by
11.408 + # propagating namespace information to the subprogram and processing it.
11.409 +
11.410 + if invoke.share_locals:
11.411 + subprogram = self.process_node(invoke.ref, self.namespace)
11.412 + else:
11.413 + subprogram = self.process_node(invoke.ref)
11.414 +
11.415 + if subprogram is not None:
11.416 + self.subprograms.append(subprogram)
11.417 + return invoke
11.418 +
11.419 +class ScopeMismatch(Exception):
11.420 + pass
11.421 +
11.422 +class NameOrganiser:
11.423 +
11.424 + """
11.425 + A local namespace which may either relate to a genuine set of function
11.426 + locals or the initialisation of a structure.
11.427 + """
11.428 +
11.429 + def __init__(self, structure=None):
11.430 +
11.431 + "Initialise the namespace with an optional 'structure'."
11.432 +
11.433 + self.structure = structure
11.434 + if structure is not None:
11.435 + self.local = "structure"
11.436 + else:
11.437 + self.local = "local"
11.438 +
11.439 + # Names may be self.local or "global".
11.440 +
11.441 + self.names = {}
11.442 +
11.443 + def make_global(self, name):
11.444 + if not self.names.has_key(name):
11.445 + self.names[name] = "global"
11.446 + elif self.names[name] == self.local:
11.447 + raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local)
11.448 +
11.449 + def find_for_load(self, name):
11.450 + return self.names.get(name, "global")
11.451 +
11.452 + def find_for_store(self, name):
11.453 + return self.names.get(name, self.local)
11.454 +
11.455 + def store(self, name):
11.456 + if self.names.get(name) != "global":
11.457 + self.names[name] = self.local
11.458 + else:
11.459 + raise ScopeMismatch, "Name '%s' already considered as global." % name
11.460 +
11.461 + def merge(self, name, scope):
11.462 + if self.names.get(name) in (None, scope):
11.463 + self.names[name] = scope
11.464 + else:
11.465 + raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.names[name])
11.466 +
11.467 + def merge_namespace(self, namespace):
11.468 + self.merge_items(namespace.names.items())
11.469 +
11.470 + def merge_items(self, items):
11.471 + for name, scope in items:
11.472 + self.merge(name, scope)
11.473 +
11.474 + def __repr__(self):
11.475 + return repr(self.names)
11.476 +
11.477 +# Convenience functions.
11.478 +
11.479 +def fix(module, builtins=None):
11.480 +
11.481 + """
11.482 + Fix the names in the given 'module', also employing the optional 'builtins'
11.483 + module, if specified.
11.484 + """
11.485 +
11.486 + fixer = Fixer()
11.487 + if builtins is not None:
11.488 + fixer.process(module, builtins)
11.489 + else:
11.490 + fixer.process(module)
11.491 +
11.492 +# vim: tabstop=4 expandtab shiftwidth=4
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/simplify/simplified/__init__.py Sun May 27 18:25:25 2007 +0200
12.3 @@ -0,0 +1,37 @@
12.4 +#!/usr/bin/env python
12.5 +
12.6 +"""
12.7 +Simplified program representation.
12.8 +
12.9 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
12.10 +
12.11 +This software is free software; you can redistribute it and/or
12.12 +modify it under the terms of the GNU General Public License as
12.13 +published by the Free Software Foundation; either version 2 of
12.14 +the License, or (at your option) any later version.
12.15 +
12.16 +This software is distributed in the hope that it will be useful,
12.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
12.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12.19 +GNU General Public License for more details.
12.20 +
12.21 +You should have received a copy of the GNU General Public
12.22 +License along with this library; see the file LICENCE.txt
12.23 +If not, write to the Free Software Foundation, Inc.,
12.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
12.25 +"""
12.26 +
12.27 +__version__ = "0.1"
12.28 +
12.29 +from simplified.ast import *
12.30 +from simplified.data import *
12.31 +from simplified.program import *
12.32 +from simplified.utils import *
12.33 +import os
12.34 +
12.35 +# Location of the built-in libraries.
12.36 +# NOTE: Change this if the package structure changes.
12.37 +
12.38 +libdir = os.path.join(os.path.split(os.path.split(__file__)[0])[0], "lib")
12.39 +
12.40 +# vim: tabstop=4 expandtab shiftwidth=4
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/simplify/simplified/ast.py Sun May 27 18:25:25 2007 +0200
13.3 @@ -0,0 +1,44 @@
13.4 +#!/usr/bin/env python
13.5 +
13.6 +"""
13.7 +Additional program AST nodes.
13.8 +
13.9 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
13.10 +
13.11 +This software is free software; you can redistribute it and/or
13.12 +modify it under the terms of the GNU General Public License as
13.13 +published by the Free Software Foundation; either version 2 of
13.14 +the License, or (at your option) any later version.
13.15 +
13.16 +This software is distributed in the hope that it will be useful,
13.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
13.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13.19 +GNU General Public License for more details.
13.20 +
13.21 +You should have received a copy of the GNU General Public
13.22 +License along with this library; see the file LICENCE.txt
13.23 +If not, write to the Free Software Foundation, Inc.,
13.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
13.25 +"""
13.26 +
13.27 +class Self:
13.28 +
13.29 + """
13.30 + A program node encapsulating object/context information in an argument list.
13.31 + This is not particularly like Attribute, Class, Instance or other such
13.32 + things, since it actually appears in the program representation.
13.33 + """
13.34 +
13.35 + def __init__(self, attribute):
13.36 + self.types = set()
13.37 + self.types.add(attribute)
13.38 +
13.39 +class Op:
13.40 +
13.41 + "A replacement AST node representing an operation in a Compare construct."
13.42 +
13.43 + def __init__(self, name, expr):
13.44 + self.name = name
13.45 + self.expr = expr
13.46 +
13.47 +# vim: tabstop=4 expandtab shiftwidth=4
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/simplify/simplified/data.py Sun May 27 18:25:25 2007 +0200
14.3 @@ -0,0 +1,237 @@
14.4 +#!/usr/bin/env python
14.5 +
14.6 +"""
14.7 +Simplified program data.
14.8 +
14.9 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
14.10 +
14.11 +This software is free software; you can redistribute it and/or
14.12 +modify it under the terms of the GNU General Public License as
14.13 +published by the Free Software Foundation; either version 2 of
14.14 +the License, or (at your option) any later version.
14.15 +
14.16 +This software is distributed in the hope that it will be useful,
14.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
14.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14.19 +GNU General Public License for more details.
14.20 +
14.21 +You should have received a copy of the GNU General Public
14.22 +License along with this library; see the file LICENCE.txt
14.23 +If not, write to the Free Software Foundation, Inc.,
14.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
14.25 +"""
14.26 +
14.27 +from simplified.utils import Structure, WithName, name
14.28 +
14.29 +# Special non-program nodes.
14.30 +
14.31 +class _Class(Structure, WithName):
14.32 +
14.33 + "A Python class."
14.34 +
14.35 + def __init__(self, *args, **kw):
14.36 + Structure.__init__(self, *args, **kw)
14.37 + WithName.__init__(self)
14.38 +
14.39 + def full_name(self):
14.40 + return "class %s" % self._full_name
14.41 +
14.42 +class SingleInstanceClass(_Class):
14.43 +
14.44 + "A Python class."
14.45 +
14.46 + def __init__(self, *args, **kw):
14.47 + _Class.__init__(self, *args, **kw)
14.48 + self.instance = None
14.49 +
14.50 + def has_instance(self, node):
14.51 + return self.instance is not None
14.52 +
14.53 + def add_instance(self, node, instance):
14.54 + self.instance = instance
14.55 +
14.56 + def get_instance(self, node):
14.57 + return self.instance
14.58 +
14.59 + def get_instance_name(self, instance):
14.60 + return self._full_name
14.61 +
14.62 + # Attribute propagation.
14.63 +
14.64 + def get_attribute_for_instance(self, attribute, instance):
14.65 + return attribute
14.66 +
14.67 +class MultipleInstanceClass(_Class):
14.68 +
14.69 + "A Python class."
14.70 +
14.71 + def __init__(self, *args, **kw):
14.72 + _Class.__init__(self, *args, **kw)
14.73 + self.instances = {}
14.74 + self.attributes_for_instances = {}
14.75 +
14.76 + def _get_key(self, node):
14.77 + return id(getattr(node, "original", None)) # self.module.original
14.78 +
14.79 + def has_instance(self, node):
14.80 + return self.instances.has_key(self._get_key(node))
14.81 +
14.82 + def add_instance(self, node, instance):
14.83 + self.instances[self._get_key(node)] = instance
14.84 +
14.85 + def get_instance(self, node):
14.86 + return self.instances[self._get_key(node)]
14.87 +
14.88 + def get_instance_name(self, instance):
14.89 + return name(instance, self._full_name)
14.90 +
14.91 + # Attribute propagation.
14.92 +
14.93 + def get_attribute_for_instance(self, attribute, instance):
14.94 +
14.95 + # Create specialised methods.
14.96 +
14.97 + if isinstance(attribute.type, Subprogram):
14.98 + subprogram = attribute.type
14.99 +
14.100 + # Each instance may have its own version of the subprogram.
14.101 +
14.102 + key = (subprogram, instance)
14.103 + if not self.attributes_for_instances.has_key(key):
14.104 + new_subprogram = subprogram.copy(instance, subprogram.full_name())
14.105 + subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
14.106 + self.attributes_for_instances[key] = Attribute(attribute.context, new_subprogram)
14.107 + print "New subprogram", new_subprogram, "for", key
14.108 +
14.109 + return self.attributes_for_instances[key]
14.110 +
14.111 + # The original nodes are returned for other attributes.
14.112 +
14.113 + else:
14.114 + return attribute
14.115 +
14.116 +class SelectiveMultipleInstanceClass(MultipleInstanceClass):
14.117 +
14.118 + "A Python class which provides multiple instances depending on the class."
14.119 +
14.120 + def _get_key(self, node):
14.121 + if self.namespace.has_key("__atomic__"):
14.122 + return id(self)
14.123 + else:
14.124 + return MultipleInstanceClass._get_key(self, node)
14.125 +
14.126 +class ProlificMultipleInstanceClass(MultipleInstanceClass):
14.127 +
14.128 + """
14.129 + A Python class which provides multiple instances for different versions of
14.130 + methods. In order to avoid unbounded instance production (since new
14.131 + instances cause new copies of methods which in turn would cause new
14.132 + instances), a relations dictionary is maintained which attempts to map
14.133 + "requesting instances" to existing instances, suggesting such instances in
14.134 + preference to new ones.
14.135 + """
14.136 +
14.137 + def __init__(self, *args, **kw):
14.138 + MultipleInstanceClass.__init__(self, *args, **kw)
14.139 + self.instance_relations = {}
14.140 +
14.141 + def _get_key(self, node):
14.142 + if self.namespace.has_key("__atomic__"):
14.143 + return id(self)
14.144 + else:
14.145 + return id(node)
14.146 +
14.147 + def has_instance(self, node):
14.148 + requesting_instance = getattr(node, "instance", None)
14.149 + #return requesting_instance is not None and requesting_instance.get_class() is self or \
14.150 + return self.instance_relations.has_key(requesting_instance) or self.instances.has_key(self._get_key(node))
14.151 +
14.152 + def add_instance(self, node, instance):
14.153 + requesting_instance = getattr(node, "instance", None)
14.154 + print "New instance", instance, "for", id(node), requesting_instance
14.155 + self.instances[self._get_key(node)] = instance
14.156 + if requesting_instance is not None:
14.157 + self.instance_relations[requesting_instance] = instance
14.158 + requesting_instance.get_class().instance_relations[instance] = requesting_instance
14.159 +
14.160 + def get_instance(self, node):
14.161 + requesting_instance = getattr(node, "instance", None)
14.162 + #if requesting_instance is not None and requesting_instance.get_class() is self:
14.163 + # return requesting_instance
14.164 + return self.instance_relations.get(requesting_instance) or self.instances[self._get_key(node)]
14.165 +
14.166 +class Instance(Structure):
14.167 +
14.168 + "An instance."
14.169 +
14.170 + def full_name(self):
14.171 + return self.get_class().get_instance_name(self)
14.172 +
14.173 + def get_class(self):
14.174 + for n in self.namespace.load("__class__"):
14.175 + return n.type
14.176 + else:
14.177 + raise ValueError, "__class__"
14.178 +
14.179 + def __repr__(self):
14.180 + return "Instance of type '%s'" % self.full_name()
14.181 +
14.182 + def __eq__(self, other):
14.183 + # NOTE: Single instance: all instances are the same
14.184 + # NOTE: Multiple instances: all instances are different
14.185 + return self.full_name() == other.full_name()
14.186 +
14.187 + def __hash__(self):
14.188 + return id(self)
14.189 +
14.190 +class Constant:
14.191 +
14.192 + "A constant initialised with a type name for future processing."
14.193 +
14.194 + def __init__(self, name, value):
14.195 + self.name = name
14.196 + self.value = value
14.197 + self.typename = self.value.__class__.__name__
14.198 +
14.199 +class Attribute:
14.200 +
14.201 + """
14.202 + An attribute abstraction, indicating the type of the attribute along with
14.203 + its context or origin.
14.204 + """
14.205 +
14.206 + def __init__(self, context, type):
14.207 + self.context = context
14.208 + self.type = type
14.209 +
14.210 + def __eq__(self, other):
14.211 + return hasattr(other, "type") and other.type == self.type or other == self.type
14.212 +
14.213 + def __repr__(self):
14.214 + return "Attribute(%s, %s)" % (repr(self.context), repr(self.type))
14.215 +
14.216 + def __hash__(self):
14.217 + return id(self.type)
14.218 +
14.219 +# Configuration setting.
14.220 +
14.221 +Class = SingleInstanceClass
14.222 +#Class = MultipleInstanceClass
14.223 +
14.224 +def set_single_instance_mode():
14.225 + global Class
14.226 + Class = SingleInstanceClass
14.227 +
14.228 +def set_multiple_instance_mode():
14.229 + global Class
14.230 + Class = MultipleInstanceClass
14.231 +
14.232 +def set_selective_multiple_instance_mode():
14.233 + global Class
14.234 + Class = SelectiveMultipleInstanceClass
14.235 +
14.236 +def set_prolific_multiple_instance_mode():
14.237 + global Class
14.238 + Class = ProlificMultipleInstanceClass
14.239 +
14.240 +# vim: tabstop=4 expandtab shiftwidth=4
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
15.2 +++ b/simplify/simplified/program.py Sun May 27 18:25:25 2007 +0200
15.3 @@ -0,0 +1,412 @@
15.4 +#!/usr/bin/env python
15.5 +
15.6 +"""
15.7 +Simplified program nodes for easier type propagation and analysis. This module
15.8 +contains nodes representing program instructions or operations, program
15.9 +structure or organisation, and abstract program data.
15.10 +
15.11 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
15.12 +
15.13 +This software is free software; you can redistribute it and/or
15.14 +modify it under the terms of the GNU General Public License as
15.15 +published by the Free Software Foundation; either version 2 of
15.16 +the License, or (at your option) any later version.
15.17 +
15.18 +This software is distributed in the hope that it will be useful,
15.19 +but WITHOUT ANY WARRANTY; without even the implied warranty of
15.20 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15.21 +GNU General Public License for more details.
15.22 +
15.23 +You should have received a copy of the GNU General Public
15.24 +License along with this library; see the file LICENCE.txt
15.25 +If not, write to the Free Software Foundation, Inc.,
15.26 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
15.27 +"""
15.28 +
15.29 +from simplified.utils import Structure, WithName, name
15.30 +import sys
15.31 +
15.32 +# Simplified program nodes.
15.33 +
15.34 +class Node:
15.35 +
15.36 + """
15.37 + A result node with common attributes:
15.38 +
15.39 + original The original node from which this node was created.
15.40 + defining Whether the node defines something in the original program.
15.41 + name Any name involved (variable or attribute).
15.42 + index Any index involved (temporary variable name).
15.43 + value Any constant value.
15.44 + ref Any reference to (for example) subprograms.
15.45 + nstype Any indication of the namespace type involved in a name access.
15.46 +
15.47 + Expression-related attributes:
15.48 +
15.49 + expr Any contributing expression.
15.50 + lvalue Any target expression.
15.51 + test Any test expression in a conditional instruction.
15.52 +
15.53 + Invocation and subprogram attributes:
15.54 +
15.55 + args Any collection of argument nodes.
15.56 + params Any collection of parameter nodes and defaults.
15.57 +
15.58 + Tuple construction attributes:
15.59 +
15.60 + nodes Any expressions used to initialise a tuple
15.61 +
15.62 + Statement-grouping attributes:
15.63 +
15.64 + body Any conditional code depending on the success of a test.
15.65 + else_ Any conditional code depending on the failure of a test.
15.66 + handler Any exception handler code.
15.67 + finally_ Any code which will be executed regardless.
15.68 + code Any unconditional code.
15.69 + choices Any choices which may be included in the final program.
15.70 + """
15.71 +
15.72 + common_attributes = "name", "index", "value", "nstype", "internal", "returns_value", "is_method", "ref", "module", "structures", "original"
15.73 + expression_attributes = "expr", "lvalue", "test"
15.74 + argument_attributes = "star", "dstar"
15.75 + invocation_attributes = "params", # not "args" - see "pos_args", "kw_args"
15.76 + grouping_attributes = "code", "body", "else_", "handler", "finally_", "choices"
15.77 +
15.78 + def __init__(self, original=None, defining=0, **kw):
15.79 +
15.80 + """
15.81 + Initialise a program node with a link to an optional 'original' AST
15.82 + node. An optional 'defining' parameter (if set to a true value), sets
15.83 + this node as the defining node in the original.
15.84 + """
15.85 +
15.86 + self.original = original
15.87 + self.defining = defining
15.88 + self.copies = {}
15.89 +
15.90 + if self.original is not None and defining:
15.91 + self.original._node = self
15.92 + for name, value in kw.items():
15.93 + setattr(self, name, value)
15.94 +
15.95 + # Annotations.
15.96 +
15.97 + self.types = set()
15.98 +
15.99 + def __repr__(self):
15.100 +
15.101 + "Return a readable representation."
15.102 +
15.103 + if hasattr(self, "full_name"):
15.104 + s = "%s '%s'" % (self.__class__.__name__, self.full_name())
15.105 + elif hasattr(self, "name"):
15.106 + s = "%s '%s'" % (self.__class__.__name__, self.name)
15.107 + elif hasattr(self, "index"):
15.108 + s = "%s (%s)" % (self.__class__.__name__, self.index)
15.109 + elif hasattr(self, "value"):
15.110 + s = "%s %s" % (self.__class__.__name__, repr(self.value))
15.111 + elif hasattr(self, "ref"):
15.112 + s = "%s '%s'" % (self.__class__.__name__, name(self.ref, self.ref.name))
15.113 + else:
15.114 + s = "%s" % (self.__class__.__name__,)
15.115 +
15.116 + # Annotations.
15.117 +
15.118 + if self.types:
15.119 + return "%s -> %s" % (s, self.types)
15.120 + else:
15.121 + return s
15.122 +
15.123 + def _pprint(self, indent, continuation, s, stream=None):
15.124 +
15.125 + """
15.126 + Print, at the given 'indent' level, with the given 'continuation' text,
15.127 + the string 's', either to the given, optional 'stream' or to standard
15.128 + output, this node's "pretty" representation.
15.129 + """
15.130 +
15.131 + stream = stream or sys.stdout
15.132 + if continuation:
15.133 + print >>stream, (" " * max(0, indent - len(continuation))) + continuation + s
15.134 + else:
15.135 + print >>stream, (" " * indent) + s
15.136 +
15.137 + def pprint(self, indent=0, continuation=None, stream=None):
15.138 +
15.139 + """
15.140 + Print, at the given, optional 'indent', with the given optional
15.141 + 'continuation' text, either to the given, optional 'stream' or to
15.142 + standard output, this node's "pretty" representation along with its
15.143 + children and their "pretty" representation (and so on).
15.144 + """
15.145 +
15.146 + stream = stream or sys.stdout
15.147 + self._pprint(indent, continuation, repr(self), stream)
15.148 +
15.149 + # Subprogram-related details.
15.150 +
15.151 + if hasattr(self, "params"):
15.152 + for name, default in self.params:
15.153 + self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
15.154 + if hasattr(self, "star") and self.star:
15.155 + name, default = self.star
15.156 + self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
15.157 + if hasattr(self, "dstar") and self.dstar:
15.158 + name, default = self.dstar
15.159 + self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
15.160 + if getattr(self, "internal", 0):
15.161 + self._pprint(indent + 2, "( ", "internal", stream=stream)
15.162 + if getattr(self, "structure", 0):
15.163 + self._pprint(indent + 2, "( ", "structure '%s'" % self.structure.name, stream=stream)
15.164 +
15.165 + # Expression-related details.
15.166 +
15.167 + if hasattr(self, "expr"):
15.168 + self.expr.pprint(indent + 2, "- ", stream=stream)
15.169 + if hasattr(self, "nodes"):
15.170 + for node in self.nodes:
15.171 + node.pprint(indent + 2, "- ", stream=stream)
15.172 + if hasattr(self, "lvalue"):
15.173 + self.lvalue.pprint(indent + 2, "->", stream=stream)
15.174 + if hasattr(self, "nstype"):
15.175 + self._pprint(indent + 2, "", self.nstype, stream=stream)
15.176 + if hasattr(self, "args"):
15.177 + for arg in self.pos_args:
15.178 + arg.pprint(indent + 2, "( ", stream=stream)
15.179 + for name, arg in self.kw_args.items():
15.180 + arg.pprint(indent + 2, "( ", stream=stream)
15.181 + if hasattr(self, "star") and self.star:
15.182 + self.star.pprint(indent + 2, "( ", stream=stream)
15.183 + if hasattr(self, "dstar") and self.dstar:
15.184 + self.dstar.pprint(indent + 2, "( ", stream=stream)
15.185 +
15.186 + # Statement-related details.
15.187 +
15.188 + if hasattr(self, "test"):
15.189 + self.test.pprint(indent + 2, "? ", stream=stream)
15.190 + for attr in self.grouping_attributes:
15.191 + if hasattr(self, attr) and getattr(self, attr):
15.192 + self._pprint(indent, "", "%s {" % attr, stream=stream)
15.193 + for node in getattr(self, attr):
15.194 + node.pprint(indent + 2, stream=stream)
15.195 + self._pprint(indent, "", "}", stream=stream)
15.196 +
15.197 + # Annotations.
15.198 +
15.199 + if hasattr(self, "accesses"):
15.200 + self._pprint(indent, "", "--------", stream=stream)
15.201 + for ref, attributes in self.accesses.items():
15.202 + self._pprint(indent + 2, "| ", "when %s: %s" % (ref, ", ".join([("%s via %s" % attr_acc) for attr_acc in attributes])), stream=stream)
15.203 + self._pprint(indent, "", "--------", stream=stream)
15.204 + if hasattr(self, "writes"):
15.205 + self._pprint(indent, "", "--------", stream=stream)
15.206 + for ref, attribute in self.writes.items():
15.207 + self._pprint(indent + 2, "| ", "when %s: %s" % (ref, attribute), stream=stream)
15.208 + self._pprint(indent, "", "--------", stream=stream)
15.209 +
15.210 + # Node discovery functions.
15.211 +
15.212 + def active(self):
15.213 +
15.214 + "Return the active copies of this node or a list containing this node."
15.215 +
15.216 + return self.copies.values() or [self]
15.217 +
15.218 + # Node manipulation functions.
15.219 +
15.220 + def copy(self, instance=None, new_name=None):
15.221 +
15.222 + """
15.223 + Perform a deep copy of the node, optionally specifying the 'instance'
15.224 + for whom the copy has been requested and a 'new_name' for the copied
15.225 + node. Return new unannotated copies of the node and its descendants.
15.226 + """
15.227 +
15.228 + # Copy the common attributes of this node.
15.229 +
15.230 + common = {}
15.231 + for attr in self.common_attributes:
15.232 + if hasattr(self, attr):
15.233 + common[attr] = getattr(self, attr)
15.234 +
15.235 + # Add new attributes specially for copies.
15.236 +
15.237 + common["instance"] = instance
15.238 +
15.239 + if new_name is not None:
15.240 + common["copy_of"] = self
15.241 + common["name"] = new_name
15.242 +
15.243 + # Instantiate the copy, avoiding side-effects with original and defining.
15.244 +
15.245 + node = self.__class__(**common)
15.246 + node.defining = self.defining
15.247 +
15.248 + # Add links to copies from originals.
15.249 +
15.250 + self.copies[instance] = node
15.251 +
15.252 + # Copy attributes of different types.
15.253 +
15.254 + for attr in self.expression_attributes:
15.255 + if hasattr(self, attr):
15.256 + n = getattr(self, attr)
15.257 + if n is None:
15.258 + n2 = n
15.259 + else:
15.260 + n2 = n.copy(instance)
15.261 + setattr(node, attr, n2)
15.262 +
15.263 + for attr in self.argument_attributes:
15.264 + if hasattr(self, attr):
15.265 + t = getattr(self, attr)
15.266 + if t is None:
15.267 + t2 = t
15.268 + else:
15.269 + name, n = t
15.270 + n2 = n.copy(instance)
15.271 + t2 = name, n2
15.272 + setattr(node, attr, t2)
15.273 +
15.274 + for attr in self.invocation_attributes:
15.275 + if hasattr(self, attr):
15.276 + l = getattr(self, attr)
15.277 + l2 = []
15.278 + for name, n in l:
15.279 + if n is None:
15.280 + l2.append((name, n))
15.281 + else:
15.282 + l2.append((name, n.copy(instance)))
15.283 + setattr(node, attr, l2)
15.284 +
15.285 + for attr in self.grouping_attributes:
15.286 + if hasattr(self, attr):
15.287 + l = getattr(self, attr)
15.288 + setattr(node, attr, [n.copy(instance) for n in l])
15.289 +
15.290 + # Arguments are usually processed further - "args" is useless.
15.291 +
15.292 + if hasattr(self, "pos_args"):
15.293 + node.pos_args = [n.copy(instance) for n in self.pos_args]
15.294 +
15.295 + if hasattr(self, "kw_args"):
15.296 + node.kw_args = {}
15.297 + for name, n in self.kw_args.items():
15.298 + node.kw_args[name] = n.copy(instance)
15.299 +
15.300 + return node
15.301 +
15.302 +# These are the supported "operations" described by simplified program nodes.
15.303 +
15.304 +class Pass(Node): "A placeholder node corresponding to pass."
15.305 +class Assign(Node): "A grouping node for assignment-related operations."
15.306 +class Keyword(Node): "A grouping node for keyword arguments."
15.307 +class Global(Node): "A global name designator."
15.308 +class Import(Node): "A module import operation."
15.309 +class LoadTemp(Node): "Load a previously-stored temporary value."
15.310 +class LoadName(Node): "Load a named object."
15.311 +class LoadAttr(Node): "Load an object attribute."
15.312 +class LoadRef(Node): "Load a reference, typically a subprogram or a constant."
15.313 +class LoadExc(Node): "Load a handled exception."
15.314 +class ResetExc(Node): "Reset the exception state."
15.315 +class StoreTemp(Node): "Store a temporary value."
15.316 +class StoreName(Node): "Associate a name with an object."
15.317 +class StoreAttr(Node): "Associate an object's attribute with a value."
15.318 +class ReleaseTemp(Node): "Release a temporary value."
15.319 +class Try(Node): "A try...except...else...finally grouping node."
15.320 +class Raise(Node): "An exception raising node."
15.321 +class Not(Node): "A negation of an expression."
15.322 +class CheckType(Node): "Check a value's type from a list of choices."
15.323 +class Return(Node): "Return an evaluated expression."
15.324 +class Invoke(Node): "An invocation."
15.325 +class MakeTuple(Node): "Make a tuple object."
15.326 +
15.327 +# There are two types of return node: return from function and return from
15.328 +# block.
15.329 +
15.330 +class ReturnFromFunction(Return):
15.331 + pass
15.332 +
15.333 +class ReturnFromBlock(Return):
15.334 + pass
15.335 +
15.336 +# NOTE: Not actually supported.
15.337 +# Additionally, yield statements act like return statements for the purposes
15.338 +# of this system.
15.339 +
15.340 +class Yield(ReturnFromFunction):
15.341 + pass
15.342 +
15.343 +# Some behaviour is set as the default in conditional nodes but may be
15.344 +# overridden.
15.345 +
15.346 +class Conditional(Node):
15.347 +
15.348 + "A conditional node consisting of a test and outcomes."
15.349 +
15.350 + def __init__(self, *args, **kw):
15.351 + self.isolate_test = 0
15.352 + Node.__init__(self, *args, **kw)
15.353 +
15.354 +# Invocations involve some more work to process calculated attributes.
15.355 +
15.356 +class InvokeFunction(Invoke):
15.357 +
15.358 + "A function or method invocation."
15.359 +
15.360 + def __init__(self, *args, **kw):
15.361 + self.args = []
15.362 + self.star = None
15.363 + self.dstar = None
15.364 + Invoke.__init__(self, *args, **kw)
15.365 + self.set_args(self.args)
15.366 + self.share_locals = 0
15.367 +
15.368 + def set_args(self, args):
15.369 +
15.370 + "Sort the 'args' into positional and keyword arguments."
15.371 +
15.372 + self.pos_args = []
15.373 + self.kw_args = {}
15.374 + add_kw = 0
15.375 + for arg in args:
15.376 + if not add_kw:
15.377 + if not isinstance(arg, Keyword):
15.378 + self.pos_args.append(arg)
15.379 + else:
15.380 + add_kw = 1
15.381 + if add_kw:
15.382 + if isinstance(arg, Keyword):
15.383 + self.kw_args[arg.name] = arg.expr
15.384 + else:
15.385 + raise TypeError, "Positional argument appears after keyword arguments in '%s'." % self
15.386 +
15.387 +class InvokeRef(Invoke):
15.388 +
15.389 + "A block or loop invocation."
15.390 +
15.391 + def __init__(self, *args, **kw):
15.392 + self.share_locals = 1
15.393 + Invoke.__init__(self, *args, **kw)
15.394 +
15.395 +# Program structure nodes.
15.396 +
15.397 +class Module(Node, Structure):
15.398 +
15.399 + "A Python module."
15.400 +
15.401 + def full_name(self):
15.402 + return "module %s" % self.name
15.403 +
15.404 +class Subprogram(Node, WithName):
15.405 +
15.406 + "A subprogram: functions, methods and loops."
15.407 +
15.408 + def __init__(self, *args, **kw):
15.409 + Node.__init__(self, *args, **kw)
15.410 + WithName.__init__(self)
15.411 + self.raises = set()
15.412 + self.returns = set()
15.413 + self.return_locals = set()
15.414 +
15.415 +# vim: tabstop=4 expandtab shiftwidth=4
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
16.2 +++ b/simplify/simplified/utils.py Sun May 27 18:25:25 2007 +0200
16.3 @@ -0,0 +1,167 @@
16.4 +#!/usr/bin/env python
16.5 +
16.6 +"""
16.7 +Simplified program utilities.
16.8 +
16.9 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
16.10 +
16.11 +This software is free software; you can redistribute it and/or
16.12 +modify it under the terms of the GNU General Public License as
16.13 +published by the Free Software Foundation; either version 2 of
16.14 +the License, or (at your option) any later version.
16.15 +
16.16 +This software is distributed in the hope that it will be useful,
16.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
16.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16.19 +GNU General Public License for more details.
16.20 +
16.21 +You should have received a copy of the GNU General Public
16.22 +License along with this library; see the file LICENCE.txt
16.23 +If not, write to the Free Software Foundation, Inc.,
16.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
16.25 +"""
16.26 +
16.27 +from compiler.visitor import ASTVisitor
16.28 +
16.29 +# Exceptions.
16.30 +
16.31 +class SimplifiedError(Exception):
16.32 +
16.33 + "An error in the annotation process."
16.34 +
16.35 + def __init__(self, exc, node, *args):
16.36 +
16.37 + """
16.38 + Initialise the error with an existing exception 'exc', the 'node' at
16.39 + which this error occurs, along with additional optional arguments.
16.40 + """
16.41 +
16.42 + Exception.__init__(self, *args)
16.43 + self.nodes = [node]
16.44 + self.exc = exc
16.45 +
16.46 + def add(self, node):
16.47 +
16.48 + "Add the given 'node' to the path of nodes leading from the exception."
16.49 +
16.50 + self.nodes.append(node)
16.51 +
16.52 + def __str__(self):
16.53 +
16.54 + "Return a string showing the principal exception details."
16.55 +
16.56 + return "%s, %s" % (self.exc, self.nodes)
16.57 +
16.58 +# Elementary visitor support.
16.59 +
16.60 +class Visitor(ASTVisitor):
16.61 +
16.62 + "A visitor base class."
16.63 +
16.64 + def __init__(self):
16.65 + ASTVisitor.__init__(self)
16.66 +
16.67 + def default(self, node, *args):
16.68 + raise SimplifiedError, (None, node)
16.69 +
16.70 + def dispatch(self, node, *args):
16.71 + return ASTVisitor.dispatch(self, node, *args)
16.72 +
16.73 + def dispatches(self, nodes, *args):
16.74 + results = []
16.75 + for node in nodes:
16.76 + results.append(self.dispatch(node, *args))
16.77 + return results
16.78 +
16.79 + def dispatch_dict(self, d, *args):
16.80 + results = {}
16.81 + for name, node in d.items():
16.82 + results[name] = self.dispatch(node, *args)
16.83 + return results
16.84 +
16.85 +# Unique name registration.
16.86 +
16.87 +class Naming:
16.88 +
16.89 + "Maintain records of unique names for each simple name."
16.90 +
16.91 + index_separator = "-"
16.92 +
16.93 + def __init__(self):
16.94 + self.names = {}
16.95 +
16.96 + def get(self, obj):
16.97 + return obj._unique_name
16.98 +
16.99 + def set(self, obj, name):
16.100 + if hasattr(obj, "_unique_name"):
16.101 + return
16.102 + if not self.names.has_key(name):
16.103 + self.names[name] = 0
16.104 + n = self.names[name] + 1
16.105 + self.names[name] = n
16.106 + obj._unique_name = "%s%s%d" % (name, self.index_separator, n)
16.107 +
16.108 +def name(obj, name):
16.109 +
16.110 + "Return a unique name for the given 'obj', indicating the base 'name'."
16.111 +
16.112 + naming.set(obj, name)
16.113 + return naming.get(obj)
16.114 +
16.115 +# Naming singleton.
16.116 +
16.117 +naming = Naming()
16.118 +
16.119 +# Named nodes are those which can be referenced in some way.
16.120 +
16.121 +class WithName:
16.122 +
16.123 + "Node naming."
16.124 +
16.125 + def __init__(self):
16.126 +
16.127 + "Initialise the object's full name."
16.128 +
16.129 + self._full_name = name(self, self.name or "$untitled")
16.130 +
16.131 + def full_name(self):
16.132 +
16.133 + "Return the object's full name."
16.134 +
16.135 + return self._full_name
16.136 +
16.137 +# Comparable nodes based on naming.
16.138 +
16.139 +class Comparable:
16.140 +
16.141 + "Comparable nodes implementing the 'full_name' method."
16.142 +
16.143 + def __eq__(self, other):
16.144 +
16.145 + "This object is equal to 'other' if the full names are the same."
16.146 +
16.147 + # NOTE: Single instance: all instances are the same
16.148 + # NOTE: Multiple instances: all instances are different
16.149 + if hasattr(other, "full_name"):
16.150 + return self.full_name() == other.full_name()
16.151 + else:
16.152 + return NotImplemented
16.153 +
16.154 + def __hash__(self):
16.155 +
16.156 + "The hash of this object is based on its full name."
16.157 +
16.158 + return hash(self.full_name())
16.159 +
16.160 +# Structure nodes indicating namespace-bearing objects.
16.161 +
16.162 +class Structure(Comparable):
16.163 +
16.164 + "A non-program node containing some kind of namespace."
16.165 +
16.166 + def __init__(self, **kw):
16.167 + for name, value in kw.items():
16.168 + setattr(self, name, value)
16.169 +
16.170 +# vim: tabstop=4 expandtab shiftwidth=4
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
17.2 +++ b/simplify/viewer.py Sun May 27 18:25:25 2007 +0200
17.3 @@ -0,0 +1,1171 @@
17.4 +#!/usr/bin/env python
17.5 +
17.6 +"""
17.7 +View annotated sources.
17.8 +
17.9 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
17.10 +
17.11 +This software is free software; you can redistribute it and/or
17.12 +modify it under the terms of the GNU General Public License as
17.13 +published by the Free Software Foundation; either version 2 of
17.14 +the License, or (at your option) any later version.
17.15 +
17.16 +This software is distributed in the hope that it will be useful,
17.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
17.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17.19 +GNU General Public License for more details.
17.20 +
17.21 +You should have received a copy of the GNU General Public
17.22 +License along with this library; see the file LICENCE.txt
17.23 +If not, write to the Free Software Foundation, Inc.,
17.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17.25 +"""
17.26 +
17.27 +from compiler.visitor import ASTVisitor
17.28 +from simplify.simplified import *
17.29 +import sys
17.30 +import os
17.31 +import textwrap
17.32 +
17.33 +# Classes.
17.34 +
17.35 +# HTML-related output production.
17.36 +
17.37 +html_header = """<?xml version="1.0" encoding="iso-8859-15"?>
17.38 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
17.39 +<html xmlns="http://www.w3.org/1999/xhtml">
17.40 +<head>
17.41 + <title>Module</title>
17.42 + <style type="text/css">
17.43 + html {
17.44 + background-color: black; color: white;
17.45 + }
17.46 +
17.47 + body {
17.48 + padding-bottom: 4em;
17.49 + font-size: 14pt; font-family: monospace;
17.50 + background-color: black; color: white;
17.51 + }
17.52 +
17.53 + .class { margin-top: 1em; margin-bottom: 1em; }
17.54 + .function { margin-top: 1em; margin-bottom: 1em; }
17.55 + .body { padding-left: 2em; }
17.56 + .keyword { color: yellow; }
17.57 + .comment { color: blue; }
17.58 + .class-name { color: cyan; }
17.59 + .function-name { color: cyan; }
17.60 + .str { color: #FF00FF; }
17.61 + .doc { color: #FF00FF; margin-top: 1em; margin-bottom: 1em; }
17.62 + .invocation a { color: white; text-decoration: none; }
17.63 +
17.64 + .popup {
17.65 + display: none; z-index: 2;
17.66 + position: absolute; top: 2ex; left: 0;
17.67 + padding: 0.2em; background-color: #000000; color: white;
17.68 + border: 2px solid #dddddd;
17.69 + }
17.70 +
17.71 + .invocations {
17.72 + padding: 0.5em; background-color: #770000;
17.73 + clear: all;
17.74 + }
17.75 +
17.76 + .types {
17.77 + padding: 0.5em; background-color: #0000FF;
17.78 + float: right;
17.79 + }
17.80 +
17.81 + .raises {
17.82 + padding: 0.5em; background-color: #7700FF;
17.83 + float: right;
17.84 + }
17.85 +
17.86 + .scopes {
17.87 + padding: 0.5em; background-color: #007700;
17.88 + float: left;
17.89 + }
17.90 +
17.91 + .non-writes, .non-accesses {
17.92 + padding: 0.5em; background-color: #FF0000;
17.93 + float: right;
17.94 + }
17.95 +
17.96 + .op,
17.97 + .name,
17.98 + .attr,
17.99 + .conditional,
17.100 + .operator,
17.101 + .iterator,
17.102 + .call,
17.103 + .returns,
17.104 + .failure
17.105 + {
17.106 + position: relative;
17.107 + }
17.108 +
17.109 + .op:hover > .popup,
17.110 + .name:hover > .popup,
17.111 + .attr:hover > .popup,
17.112 + .conditional:hover > .popup,
17.113 + .operator:hover > .popup,
17.114 + .iterator:hover > .popup,
17.115 + .call:hover > .popup,
17.116 + .returns:hover > .popup,
17.117 + .failure:hover > .popup
17.118 + {
17.119 + display: block;
17.120 + }
17.121 +
17.122 + </style>
17.123 +</head>
17.124 +<body>
17.125 +"""
17.126 +
17.127 +html_footer = """</body>
17.128 +</html>
17.129 +"""
17.130 +
17.131 +# Browser classes.
17.132 +
17.133 +class Browser(ASTVisitor):
17.134 +
17.135 + """
17.136 + A browsing visitor for AST nodes.
17.137 +
17.138 + Covered: Add, And, Assert, AssAttr, AssList, AssName, AssTuple, Assign,
17.139 + AugAssign, Bitand, Break, CallFunc, Class, Compare, Const,
17.140 + Continue, Dict, Discard, Div, FloorDiv, For, From, Function,
17.141 + Getattr, Global, If, Import, Keyword, Lambda, List, ListComp,
17.142 + ListCompFor, ListCompIf, Mod, Module, Mul, Name, Not, Or, Pass,
17.143 + Power, Print, Printnl, Raise, Return, Slice, Sliceobj, Stmt, Sub,
17.144 + Subscript, TryExcept, TryFinally, Tuple, UnaryAdd, UnarySub, While.
17.145 +
17.146 + Missing: Backquote, Bitor, Bitxor, Decorators, Ellipsis,
17.147 + Exec, Invert, LeftShift, RightShift, Yield.
17.148 + """
17.149 +
17.150 + def __init__(self, stream):
17.151 + ASTVisitor.__init__(self)
17.152 + self.visitor = self
17.153 + self.stream = stream
17.154 +
17.155 + def process(self, module):
17.156 + self.stream.write(html_header)
17.157 + self.dispatch(module)
17.158 + self.stream.write(html_footer)
17.159 +
17.160 + def visitModule(self, node):
17.161 + self.default(node)
17.162 +
17.163 + # Statements.
17.164 +
17.165 + def visitAssert(self, node):
17.166 + self.stream.write("<div class='assert'>\n")
17.167 + self.stream.write("<span class='failure'>\n")
17.168 + self._keyword("assert")
17.169 + self._popup(
17.170 + self._types(node._raises.active())
17.171 + )
17.172 + self.stream.write("</span>\n")
17.173 + self.dispatch(node.test)
17.174 + if node.fail:
17.175 + self.stream.write(", ")
17.176 + self.dispatch(node.fail)
17.177 + self.stream.write("</div>\n")
17.178 +
17.179 + def visitAssign(self, node):
17.180 + self.stream.write("<div class='assign'>\n")
17.181 + for lvalue in node.nodes:
17.182 + self.dispatch(lvalue)
17.183 + self.stream.write("=\n")
17.184 + self.dispatch(node.expr)
17.185 + self.stream.write("</div>\n")
17.186 +
17.187 + def visitAugAssign(self, node):
17.188 + self.stream.write("<div class='augassign'>\n")
17.189 + self.dispatch(node.node)
17.190 + self.stream.write("<span class='operator'>\n")
17.191 + self.stream.write("%s\n" % node.op)
17.192 + self._popup(
17.193 + self._invocations(node._op_call.active())
17.194 + )
17.195 + self.stream.write("</span>\n")
17.196 + self.dispatch(node.expr)
17.197 + self.stream.write("</div>\n")
17.198 +
17.199 + def visitBreak(self, node):
17.200 + self.stream.write("<div class='break'>\n")
17.201 + self._keyword("break")
17.202 + self.stream.write("</div>\n")
17.203 +
17.204 + def visitClass(self, node):
17.205 + definition = node._node
17.206 + definitions = definition.active()
17.207 + structure = definition.expr.ref
17.208 + self.stream.write("<div class='class' id='%s'>\n" % structure.full_name())
17.209 + self.stream.write("<div>\n")
17.210 + self._keyword("class")
17.211 + self._name_start(structure.name, "class-name")
17.212 + self._popup(
17.213 + self._scopes(definitions)
17.214 + )
17.215 + self._name_end()
17.216 + bases = structure.bases
17.217 +
17.218 + # Suppress the "object" class appearing alone.
17.219 +
17.220 + if bases and not (len(bases) == 1 and bases[0].name == "object"):
17.221 + self.stream.write("(")
17.222 + first = 1
17.223 + for base in bases:
17.224 + if not first:
17.225 + self.stream.write(",\n")
17.226 + self._name_start(base.name)
17.227 + self._popup(
17.228 + self._scopes([base]) +
17.229 + self._types([base])
17.230 + )
17.231 + self._name_end()
17.232 + first = 0
17.233 + self.stream.write(")")
17.234 +
17.235 + self.stream.write(":\n")
17.236 + self._comment(self._text(structure.full_name()))
17.237 + self.stream.write("</div>\n")
17.238 +
17.239 + self.stream.write("<div class='body'>\n")
17.240 + self._doc(node)
17.241 + self.dispatch(node.code)
17.242 + self.stream.write("</div>\n")
17.243 + self.stream.write("</div>\n")
17.244 +
17.245 + def visitContinue(self, node):
17.246 + self.stream.write("<div class='continue'>\n")
17.247 + self._keyword("continue")
17.248 + self.stream.write("</div>\n")
17.249 +
17.250 + def visitDiscard(self, node):
17.251 + self.stream.write("<div class='discard'>\n")
17.252 + self.default(node)
17.253 + self.stream.write("</div>\n")
17.254 +
17.255 + def visitFor(self, node):
17.256 + self.stream.write("<div class='if'>\n")
17.257 + self.stream.write("<div>\n")
17.258 + self.stream.write("<span class='iterator'>\n")
17.259 + self._keyword("for")
17.260 + self._popup(
17.261 + self._invocations(node._next_call.active())
17.262 + )
17.263 + self.stream.write("</span>\n")
17.264 + self.dispatch(node.assign)
17.265 + self.stream.write("<span class='iterator'>\n")
17.266 + self._keyword("in")
17.267 + self._popup(
17.268 + self._invocations(node._iter_call.active())
17.269 + )
17.270 + self.stream.write("</span>\n")
17.271 + self.dispatch(node.list)
17.272 + self.stream.write(":\n")
17.273 + self.stream.write("</div>\n")
17.274 + self.stream.write("<div class='body'>\n")
17.275 + self.dispatch(node.body)
17.276 + self.stream.write("</div>\n")
17.277 + if node.else_ is not None:
17.278 + self.stream.write("<div>\n")
17.279 + self._keyword("else")
17.280 + self.stream.write(":\n")
17.281 + self.stream.write("</div>\n")
17.282 + self.stream.write("<div class='body'>\n")
17.283 + self.dispatch(node.else_)
17.284 + self.stream.write("</div>\n")
17.285 + self.stream.write("</div>\n")
17.286 +
17.287 + def visitFrom(self, node):
17.288 + self.stream.write("<div class='from'>\n")
17.289 + self._keyword("from")
17.290 + self.stream.write("<span class='name'>\n")
17.291 + self.stream.write(node.modname)
17.292 + self._popup(
17.293 + self._types(node._modname.active())
17.294 + )
17.295 + self.stream.write("</span>\n")
17.296 + self._keyword("import")
17.297 + first = 1
17.298 + for (name, alias), _name in map(None, node.names, node._names):
17.299 + if not first:
17.300 + self.stream.write(",\n")
17.301 + if alias:
17.302 + self.stream.write(name + " ")
17.303 + self._keyword("as")
17.304 + self.stream.write("<span class='name'>\n")
17.305 + self.stream.write(alias or name)
17.306 + self._popup(
17.307 + self._types([_name])
17.308 + )
17.309 + self.stream.write("</span>\n")
17.310 + first = 0
17.311 + self.stream.write("</div>\n")
17.312 +
17.313 + def visitFunction(self, node):
17.314 + definition = node._node
17.315 + definitions = [n for n in definition.active() if not isinstance(n, Subprogram)]
17.316 + subprogram = node._subprogram
17.317 + subprograms = subprogram.active()
17.318 + self.stream.write("<div class='function' id='%s'>\n" % subprogram.full_name())
17.319 + self.stream.write("<div>\n")
17.320 + self._keyword("def")
17.321 + self._name_start(subprogram.name, "function-name")
17.322 + self._popup(
17.323 + self._scopes([definition]) + # not dependent on subprograms
17.324 + self._raises(subprograms)
17.325 + )
17.326 + self._name_end()
17.327 + self.stream.write("(")
17.328 + self._parameters(subprogram, subprograms)
17.329 + self.stream.write(")")
17.330 + self.stream.write(":\n")
17.331 + self._comment(self._text(subprogram.full_name()))
17.332 + self.stream.write("</div>\n")
17.333 +
17.334 + self.stream.write("<div class='body'>\n")
17.335 + self._doc(node)
17.336 + self.dispatch(node.code)
17.337 + self.stream.write("</div>\n")
17.338 + self.stream.write("</div>\n")
17.339 +
17.340 + def visitGlobal(self, node):
17.341 + self.stream.write("<div class='global'>\n")
17.342 + self._keyword("global")
17.343 + first = 1
17.344 + for name in node.names:
17.345 + if not first:
17.346 + self.stream.write(",\n")
17.347 + self.stream.write(name)
17.348 + first = 0
17.349 + self.stream.write("</div>\n")
17.350 +
17.351 + def visitIf(self, node):
17.352 + self.stream.write("<div class='if'>\n")
17.353 + first = 1
17.354 + conditional = node._node
17.355 + conditionals = conditional.active()
17.356 + for compare, stmt in node.tests:
17.357 + self.stream.write("<div>\n")
17.358 + self.stream.write("<span class='conditional'>\n")
17.359 + if first:
17.360 + self._keyword("if")
17.361 + else:
17.362 + self._keyword("elif")
17.363 + self._popup(
17.364 + self._invocations([c.test for c in conditionals])
17.365 + )
17.366 + self.stream.write("</span>\n")
17.367 + self.dispatch(compare)
17.368 + self.stream.write(":\n")
17.369 + self.stream.write("</div>\n")
17.370 + self.stream.write("<div class='body'>\n")
17.371 + self.dispatch(stmt)
17.372 + self.stream.write("</div>\n")
17.373 + if conditional.else_:
17.374 + conditional = conditional.else_[0]
17.375 + conditionals = conditional.active()
17.376 + else:
17.377 + conditional = None
17.378 + conditionals = []
17.379 + first = 0
17.380 + if node.else_ is not None:
17.381 + self.stream.write("<div>\n")
17.382 + self._keyword("else")
17.383 + self.stream.write(":\n")
17.384 + self.stream.write("</div>\n")
17.385 + self.stream.write("<div class='body'>\n")
17.386 + self.dispatch(node.else_)
17.387 + self.stream.write("</div>\n")
17.388 + self.stream.write("</div>\n")
17.389 +
17.390 + def visitImport(self, node):
17.391 + self.stream.write("<div class='import'>\n")
17.392 + self._keyword("import")
17.393 + first = 1
17.394 + for (name, alias), _name in map(None, node.names, node._names):
17.395 + if not first:
17.396 + self.stream.write(",\n")
17.397 + if alias:
17.398 + self.stream.write(name + " ")
17.399 + self._keyword("as")
17.400 + self.stream.write("<span class='name'>\n")
17.401 + self.stream.write(alias or name)
17.402 + self._popup(
17.403 + self._types([_name])
17.404 + )
17.405 + self.stream.write("</span>\n")
17.406 + first = 0
17.407 + self.stream.write("</div>\n")
17.408 +
17.409 + def visitPass(self, node):
17.410 + self.stream.write("<div class='pass'>\n")
17.411 + self._keyword("pass")
17.412 + self.stream.write("</div>\n")
17.413 +
17.414 + def visitPrint(self, node):
17.415 + self.stream.write("<div class='print'>\n")
17.416 + self._keyword("print")
17.417 + if node.dest is not None:
17.418 + self.stream.write(">>\n")
17.419 + self.dispatch(node.dest)
17.420 + for n in node.nodes:
17.421 + self.dispatch(n)
17.422 + self.stream.write(",\n")
17.423 + self.stream.write("</div>\n")
17.424 +
17.425 + def visitPrintnl(self, node):
17.426 + self.stream.write("<div class='printnl'>\n")
17.427 + self._keyword("print")
17.428 + if node.dest is not None:
17.429 + self.stream.write(">>\n")
17.430 + self.dispatch(node.dest)
17.431 + first = 1
17.432 + for n in node.nodes:
17.433 + if not first:
17.434 + self.stream.write(",\n")
17.435 + self.dispatch(n)
17.436 + first = 0
17.437 + self.stream.write("</div>\n")
17.438 +
17.439 + def visitRaise(self, node):
17.440 + target = node._node.expr
17.441 + targets = target.active()
17.442 + self.stream.write("<div class='raise'>\n")
17.443 + self.stream.write("<span class='call'>\n")
17.444 + self._keyword("raise")
17.445 + self._popup(
17.446 + self._invocations(targets)
17.447 + )
17.448 + self.stream.write("</span>\n")
17.449 + self.dispatch(node.expr1)
17.450 + if node.expr2 is not None:
17.451 + self.stream.write(",\n")
17.452 + self.dispatch(node.expr2)
17.453 + if node.expr3 is not None:
17.454 + self.stream.write(",\n")
17.455 + self.dispatch(node.expr3)
17.456 + self.stream.write("</div>\n")
17.457 +
17.458 + def visitReturn(self, node):
17.459 + value = node._node
17.460 + values = value.active()
17.461 + self.stream.write("<div class='return'>\n")
17.462 + self.stream.write("<span class='returns'>\n")
17.463 + self._keyword("return")
17.464 + self._popup(
17.465 + self._types(values)
17.466 + )
17.467 + self.stream.write("</span>\n")
17.468 + self.dispatch(node.value)
17.469 + self.stream.write("</div>\n")
17.470 +
17.471 + def visitStmt(self, node):
17.472 + self.stream.write("<div class='stmt'>\n")
17.473 + self.default(node)
17.474 + self.stream.write("</div>\n")
17.475 +
17.476 + def visitTryExcept(self, node):
17.477 + self.stream.write("<div class='tryexcept'>\n")
17.478 + self.stream.write("<div>\n")
17.479 + self._keyword("try")
17.480 + self.stream.write(":\n")
17.481 + self.stream.write("</div>\n")
17.482 + self.stream.write("<div class='body'>\n")
17.483 + self.dispatch(node.body)
17.484 + self.stream.write("</div>\n")
17.485 + for spec, assign, statement in node.handlers:
17.486 + self.stream.write("<div>\n")
17.487 + self._keyword("except")
17.488 + if spec is not None:
17.489 + self.dispatch(spec)
17.490 + if assign is not None:
17.491 + self.stream.write(",\n")
17.492 + self.dispatch(assign)
17.493 + self.stream.write(":\n")
17.494 + self.stream.write("</div>\n")
17.495 + self.stream.write("<div class='body'>\n")
17.496 + self.dispatch(statement)
17.497 + self.stream.write("</div>\n")
17.498 + if node.else_ is not None:
17.499 + self.stream.write("<div>\n")
17.500 + self._keyword("else")
17.501 + self.stream.write(":\n")
17.502 + self.stream.write("</div>\n")
17.503 + self.stream.write("<div class='body'>\n")
17.504 + self.dispatch(node.else_)
17.505 + self.stream.write("</div>\n")
17.506 + self.stream.write("</div>\n")
17.507 +
17.508 + def visitTryFinally(self, node):
17.509 + self.stream.write("<div class='tryfinally'>\n")
17.510 + self.stream.write("<div>\n")
17.511 + self._keyword("try")
17.512 + self.stream.write(":\n")
17.513 + self.stream.write("</div>\n")
17.514 + self.stream.write("<div class='body'>\n")
17.515 + self.dispatch(node.body)
17.516 + self.stream.write("</div>\n")
17.517 + self.stream.write("<div>\n")
17.518 + self._keyword("finally")
17.519 + self.stream.write(":\n")
17.520 + self.stream.write("</div>\n")
17.521 + self.stream.write("<div class='body'>\n")
17.522 + self.dispatch(node.final)
17.523 + self.stream.write("</div>\n")
17.524 + self.stream.write("</div>\n")
17.525 +
17.526 + def visitWhile(self, node):
17.527 + self.stream.write("<div class='while'>\n")
17.528 + self.stream.write("<div>\n")
17.529 + self.stream.write("<span class='conditional'>\n")
17.530 + self._keyword("while")
17.531 + self._popup(
17.532 + self._invocations(node._test_call.active())
17.533 + )
17.534 + self.stream.write("</span>\n")
17.535 + self.dispatch(node.test)
17.536 + self.stream.write(":\n")
17.537 + self.stream.write("</div>\n")
17.538 + self.stream.write("<div class='body'>\n")
17.539 + self.dispatch(node.body)
17.540 + self.stream.write("</div>\n")
17.541 + if node.else_ is not None:
17.542 + self.stream.write("<div>\n")
17.543 + self._keyword("else")
17.544 + self.stream.write(":\n")
17.545 + self.stream.write("</div>\n")
17.546 + self.stream.write("<div class='body'>\n")
17.547 + self.dispatch(node.else_)
17.548 + self.stream.write("</div>\n")
17.549 + self.stream.write("</div>\n")
17.550 +
17.551 + # Expression-related helper methods.
17.552 +
17.553 + def _visitBinary(self, node, name, symbol):
17.554 + self.stream.write("<span class='%s'>\n" % name)
17.555 + self.dispatch(node.left)
17.556 + self.stream.write("<span class='operator'>\n")
17.557 + self.stream.write(self._text(symbol))
17.558 + self._popup(
17.559 + self._invocations(node._left_call.active() + node._right_call.active())
17.560 + )
17.561 + self.stream.write("</span>\n")
17.562 + self.dispatch(node.right)
17.563 + self.stream.write("</span>")
17.564 +
17.565 + def _visitUnary(self, node, name, symbol):
17.566 + self.stream.write("<span class='%s'>\n" % name)
17.567 + self.stream.write("<span class='operator'>\n")
17.568 + self.stream.write(symbol)
17.569 + self._popup(
17.570 + self._invocations(node._unary_call.active())
17.571 + )
17.572 + self.stream.write("</span>\n")
17.573 + self.dispatch(node.expr)
17.574 + self.stream.write("</span>")
17.575 +
17.576 + # Expressions.
17.577 +
17.578 + def visitAdd(self, node):
17.579 + self._visitBinary(node, "add", "+")
17.580 +
17.581 + def visitAnd(self, node):
17.582 + self.stream.write("<span class='and'>\n")
17.583 + first = 1
17.584 + for n in node.nodes:
17.585 + if not first:
17.586 + self._keyword("and")
17.587 + self.dispatch(n)
17.588 + first = 0
17.589 + self.stream.write("</span>")
17.590 +
17.591 + def visitAssAttr(self, node):
17.592 + target = node._node
17.593 + targets = target.active()
17.594 + self.stream.write("<span class='assattr'>\n")
17.595 + self.dispatch(node.expr)
17.596 + self.stream.write("<span class='attr'>\n")
17.597 + self.stream.write(".%s\n" % self._text(node.attrname))
17.598 + self._popup(
17.599 + self._scopes(targets) +
17.600 + self._types(targets)
17.601 + )
17.602 + self.stream.write("</span>\n")
17.603 + self.stream.write("</span>\n")
17.604 +
17.605 + def visitAssList(self, node):
17.606 + self.stream.write("<span class='list'>\n")
17.607 + self.stream.write("[")
17.608 + self._sequence(node)
17.609 + self.stream.write("]\n")
17.610 + self.stream.write("</span>\n")
17.611 +
17.612 + def visitAssName(self, node):
17.613 + target = node._node
17.614 + targets = target.active()
17.615 + self._name_start(target.name)
17.616 + self._popup(
17.617 + self._scopes(targets) +
17.618 + self._types(targets)
17.619 + )
17.620 + self._name_end()
17.621 +
17.622 + def visitAssTuple(self, node):
17.623 + self.stream.write("<span class='tuple'>\n")
17.624 + self.stream.write("(")
17.625 + self._sequence(node)
17.626 + self.stream.write(")\n")
17.627 + self.stream.write("</span>\n")
17.628 +
17.629 + def visitBitand(self, node):
17.630 + self.stream.write("<span class='bitand'>\n")
17.631 + self.dispatch(node.nodes[0])
17.632 + for op in node._ops:
17.633 + self.stream.write("<span class='op'>\n")
17.634 + self.stream.write(self._text(op.name))
17.635 + self._popup(
17.636 + self._op(op)
17.637 + )
17.638 + self.stream.write("</span>\n")
17.639 + self.dispatch(op.expr)
17.640 + self.stream.write("</span>")
17.641 +
17.642 + def visitCallFunc(self, node):
17.643 + target = node._node
17.644 + targets = target.active()
17.645 + self.stream.write("<span class='callfunc'>\n")
17.646 + self.dispatch(node.node)
17.647 + self.stream.write("<span class='call'>\n")
17.648 + self.stream.write("(")
17.649 + self._popup(
17.650 + self._invocations(targets)
17.651 + )
17.652 + self.stream.write("</span>\n")
17.653 + first = 1
17.654 + for arg in node.args:
17.655 + if not first:
17.656 + self.stream.write(",\n")
17.657 + self.dispatch(arg)
17.658 + first = 0
17.659 + if node.star_args is not None:
17.660 + if not first:
17.661 + self.stream.write(", *\n")
17.662 + self.dispatch(node.star_args)
17.663 + first = 0
17.664 + if node.dstar_args is not None:
17.665 + if not first:
17.666 + self.stream.write(", **\n")
17.667 + self.dispatch(node.dstar_args)
17.668 + first = 0
17.669 + self.stream.write(")\n")
17.670 + self.stream.write("</span>\n")
17.671 +
17.672 + def visitCompare(self, node):
17.673 + self.stream.write("<span class='compare'>\n")
17.674 + self.dispatch(node.expr)
17.675 + for op in node._ops:
17.676 + self.stream.write("<span class='op'>\n")
17.677 + self.stream.write(self._text(op.name))
17.678 + self._popup(
17.679 + self._op(op)
17.680 + )
17.681 + self.stream.write("</span>\n")
17.682 + self.dispatch(op.expr)
17.683 + self.stream.write("</span>\n")
17.684 +
17.685 + def visitConst(self, node):
17.686 + if isinstance(node.value, (str, unicode)):
17.687 + self.stream.write("<span class='str'>\n")
17.688 + self.stream.write(repr(node.value))
17.689 + if isinstance(node.value, (str, unicode)):
17.690 + self.stream.write("</span>\n")
17.691 +
17.692 + def visitDict(self, node):
17.693 + self.stream.write("<span class='dict'>\n")
17.694 + self.stream.write("{")
17.695 + self._mapping(node)
17.696 + self.stream.write("}\n")
17.697 + self.stream.write("</span>\n")
17.698 +
17.699 + def visitDiv(self, node):
17.700 + self._visitBinary(node, "div", "/")
17.701 +
17.702 + def visitFloorDiv(self, node):
17.703 + self._visitBinary(node, "floordiv", "//")
17.704 +
17.705 + def visitGetattr(self, node):
17.706 + target = node._node
17.707 + targets = target.active()
17.708 + self.stream.write("<span class='getattr'>\n")
17.709 + self.dispatch(node.expr)
17.710 + self.stream.write("<span class='attr'>\n")
17.711 + self.stream.write(".%s\n" % self._text(node.attrname))
17.712 + self._popup(
17.713 + self._scopes(targets) +
17.714 + self._types(targets)
17.715 + )
17.716 + self.stream.write("</span>\n")
17.717 + self.stream.write("</span>\n")
17.718 +
17.719 + def visitKeyword(self, node):
17.720 + self.stream.write("<span class='keyword-arg'>\n")
17.721 + self.stream.write(node.name)
17.722 + self.stream.write("=")
17.723 + self.dispatch(node.expr)
17.724 + self.stream.write("</span>\n")
17.725 +
17.726 + def visitLambda(self, node):
17.727 + definition = node._node
17.728 + definitions = [n for n in definition.active() if not isinstance(n, Subprogram)]
17.729 + subprogram = node._subprogram
17.730 + subprograms = subprogram.active()
17.731 + self.stream.write("<span class='lambda'>\n")
17.732 + self._keyword("lambda")
17.733 + self._parameters(subprogram, subprograms)
17.734 + self.dispatch(node.code)
17.735 + self.stream.write("</span>\n")
17.736 +
17.737 + visitList = visitAssList
17.738 +
17.739 + def visitListComp(self, node):
17.740 + self.stream.write("<span class='listcomp'>\n")
17.741 + self.stream.write("[")
17.742 + self.dispatch(node.expr)
17.743 + for qual in node.quals:
17.744 + self.dispatch(qual)
17.745 + self.stream.write("]\n")
17.746 + self.stream.write("</span>\n")
17.747 +
17.748 + def visitListCompFor(self, node):
17.749 + self.stream.write("<span class='listcompfor'>\n")
17.750 + self.stream.write("<span class='iterator'>\n")
17.751 + self._keyword("for")
17.752 + self._popup(
17.753 + self._invocations(node._next_call.active())
17.754 + )
17.755 + self.stream.write("</span>\n")
17.756 + self.dispatch(node.assign)
17.757 + self.stream.write("<span class='iterator'>\n")
17.758 + self._keyword("in")
17.759 + self._popup(
17.760 + self._invocations(node._iter_call.active())
17.761 + )
17.762 + self.stream.write("</span>\n")
17.763 + self.dispatch(node.list)
17.764 + for if_ in node.ifs:
17.765 + self.dispatch(if_)
17.766 + self.stream.write("</span>\n")
17.767 +
17.768 + def visitListCompIf(self, node):
17.769 + conditional = node._node
17.770 + conditionals = conditional.active()
17.771 + self.stream.write("<span class='listcompif'>\n")
17.772 + self.stream.write("<span class='conditional'>\n")
17.773 + self._keyword("if")
17.774 + self._popup(
17.775 + self._invocations([c.test for c in conditionals])
17.776 + )
17.777 + self.stream.write("</span>\n")
17.778 + self.dispatch(node.test)
17.779 + self.stream.write("</span>\n")
17.780 +
17.781 + def visitMod(self, node):
17.782 + self._visitBinary(node, "mod", "%")
17.783 +
17.784 + def visitMul(self, node):
17.785 + self._visitBinary(node, "mul", "*")
17.786 +
17.787 + def visitName(self, node):
17.788 + target = node._node
17.789 + targets = target.active()
17.790 + self._name_start(target.name)
17.791 + self._popup(
17.792 + self._scopes(targets) +
17.793 + self._types(targets)
17.794 + )
17.795 + self._name_end()
17.796 +
17.797 + def visitNot(self, node):
17.798 + self.stream.write("<span class='not'>\n")
17.799 + self._keyword("not")
17.800 + self.dispatch(node.expr)
17.801 + self.stream.write("</span>")
17.802 +
17.803 + def visitOr(self, node):
17.804 + self.stream.write("<span class='or'>\n")
17.805 + first = 1
17.806 + for n in node.nodes:
17.807 + if not first:
17.808 + self._keyword("or")
17.809 + self.dispatch(n)
17.810 + first = 0
17.811 + self.stream.write("</span>")
17.812 +
17.813 + def visitPower(self, node):
17.814 + self._visitBinary(node, "power", "**")
17.815 +
17.816 + def visitSlice(self, node):
17.817 + target = node._node
17.818 + targets = target.active()
17.819 + self.stream.write("<span class='slice'>\n")
17.820 + self.dispatch(node.expr)
17.821 + self.stream.write("<span class='call'>\n")
17.822 + self.stream.write("[")
17.823 + self._popup(
17.824 + self._invocations(targets)
17.825 + )
17.826 + self.stream.write("</span>\n")
17.827 + if node.lower:
17.828 + self.dispatch(node.lower)
17.829 + self.stream.write(":")
17.830 + if node.upper:
17.831 + self.dispatch(node.upper)
17.832 + # NOTE: Step?
17.833 + self.stream.write("]")
17.834 + self.stream.write("</span>\n")
17.835 +
17.836 + def visitSliceobj(self, node):
17.837 + self.stream.write("<span class='sliceobj'>\n")
17.838 + first = 1
17.839 + for n in node.nodes:
17.840 + if not first:
17.841 + self.stream.write(":")
17.842 + self.dispatch(n)
17.843 + self.stream.write("</span>\n")
17.844 +
17.845 + def visitSub(self, node):
17.846 + self._visitBinary(node, "sub", "-")
17.847 +
17.848 + def visitSubscript(self, node):
17.849 + target = node._node
17.850 + targets = target.active()
17.851 + self.stream.write("<span class='subscript'>\n")
17.852 + self.dispatch(node.expr)
17.853 + self.stream.write("<span class='call'>\n")
17.854 + self.stream.write("[")
17.855 + self._popup(
17.856 + self._invocations(targets)
17.857 + )
17.858 + self.stream.write("</span>\n")
17.859 + first = 1
17.860 + for sub in node.subs:
17.861 + if not first:
17.862 + self.stream.write(", ")
17.863 + self.dispatch(sub)
17.864 + first = 0
17.865 + self.stream.write("]")
17.866 + self.stream.write("</span>\n")
17.867 +
17.868 + visitTuple = visitAssTuple
17.869 +
17.870 + def visitUnaryAdd(self, node):
17.871 + self._visitUnary(node, "add", "+")
17.872 +
17.873 + def visitUnarySub(self, node):
17.874 + self._visitUnary(node, "sub", "-")
17.875 +
17.876 + # Output preparation methods.
17.877 +
17.878 + def _text(self, text):
17.879 + return text.replace("&", "&").replace("<", "<").replace(">", ">")
17.880 +
17.881 + def _attr(self, attr):
17.882 + return self._text(attr).replace("'", "'").replace('"', """)
17.883 +
17.884 + def _url(self, url):
17.885 + return self._attr(url).replace("#", "%23").replace("-", "%2d")
17.886 +
17.887 + def _comment(self, comment):
17.888 + self.stream.write("<span class='comment'># %s</span>\n" % comment)
17.889 +
17.890 + def _keyword(self, kw):
17.891 + self.stream.write("<span class='keyword'>%s</span> " % kw)
17.892 +
17.893 + def _doc(self, node):
17.894 + if node.doc is not None:
17.895 + self.stream.write("<pre class='doc'>\n")
17.896 + self.stream.write('"""')
17.897 + output = textwrap.dedent(node.doc.replace('"""', '\\"\\"\\"'))
17.898 + self.stream.write(self._text(output))
17.899 + self.stream.write('"""')
17.900 + self.stream.write("</pre>\n")
17.901 +
17.902 + def _sequence(self, node):
17.903 + first = 1
17.904 + for n in node.nodes:
17.905 + if not first:
17.906 + self.stream.write(",\n")
17.907 + self.dispatch(n)
17.908 + first = 0
17.909 +
17.910 + def _mapping(self, node):
17.911 + first = 1
17.912 + for k, v in node.items:
17.913 + if not first:
17.914 + self.stream.write(",\n")
17.915 + self.dispatch(k)
17.916 + self.stream.write(":\n")
17.917 + self.dispatch(v)
17.918 + first = 0
17.919 +
17.920 + def _parameters(self, subprogram, subprograms):
17.921 +
17.922 + # Get all the parameter lists.
17.923 +
17.924 + params = []
17.925 + nparams = 0
17.926 + for sub in subprograms:
17.927 + params.append(sub.params)
17.928 + nparams = max(nparams, len(sub.params))
17.929 + stars = []
17.930 + have_star = 0
17.931 + for sub in subprograms:
17.932 + stars.append(sub.star)
17.933 + if sub.star is not None:
17.934 + have_star = 1
17.935 + dstars = []
17.936 + have_dstar = 0
17.937 + for sub in subprograms:
17.938 + dstars.append(sub.dstar)
17.939 + if sub.dstar is not None:
17.940 + have_dstar = 1
17.941 +
17.942 + # Traverse the parameter lists, choosing a "column" at a time.
17.943 +
17.944 + first = 1
17.945 + for n in range(0, nparams):
17.946 + if not first:
17.947 + self.stream.write(",\n")
17.948 + main_param, main_default = subprogram.params[n]
17.949 + self._name_start(main_param)
17.950 + self._popup(
17.951 + self._parameter(subprograms, params, n)
17.952 + )
17.953 + self._name_end()
17.954 + self._default(main_default)
17.955 + first = 0
17.956 +
17.957 + if have_star:
17.958 + if not first:
17.959 + self.stream.write(", *\n")
17.960 + main_param, main_default = subprogram.star
17.961 + self._name_start(main_param)
17.962 + self._popup(
17.963 + self._parameter(subprograms, stars)
17.964 + )
17.965 + self._name_end()
17.966 + self._default(main_default)
17.967 + first = 0
17.968 +
17.969 + if have_dstar:
17.970 + if not first:
17.971 + self.stream.write(", **\n")
17.972 + main_param, main_default = subprogram.dstar
17.973 + self._name_start(main_param)
17.974 + self._popup(
17.975 + self._parameter(subprograms, dstars)
17.976 + )
17.977 + self._name_end()
17.978 + self._default(main_default)
17.979 + first = 0
17.980 +
17.981 + def _parameter(self, subprograms, params, n=None):
17.982 + types = set()
17.983 + for i in range(0, len(subprograms)):
17.984 + subprogram = subprograms[i]
17.985 + if n is not None:
17.986 + param, default = params[i][n]
17.987 + else:
17.988 + param, default = params[i]
17.989 + if hasattr(subprogram, "paramtypes"):
17.990 + types.update(subprogram.paramtypes[param])
17.991 + return self._types_container(types, "types")
17.992 +
17.993 + def _default(self, default):
17.994 + if default is not None and default.original is not None:
17.995 + self.stream.write("=\n")
17.996 + self.dispatch(default.original)
17.997 +
17.998 + def _name(self, name):
17.999 + self.stream.write("<span class='name'>%s</span>\n" % name)
17.1000 +
17.1001 + def _name_start(self, name, classes=None):
17.1002 + if classes is not None:
17.1003 + classes = " " + classes
17.1004 + else:
17.1005 + classes = ""
17.1006 + self.stream.write("<span class='name%s'>%s\n" % (classes, name))
17.1007 +
17.1008 + def _name_end(self):
17.1009 + self.stream.write("</span>\n")
17.1010 +
17.1011 + def _popup(self, info):
17.1012 + if info:
17.1013 + self.stream.write("<span class='popup'>\n")
17.1014 + for section, subsection, labels in info:
17.1015 + self.stream.write("<div class='%s'>\n" % section)
17.1016 + for label in labels:
17.1017 + self.stream.write("<div class='%s'>\n" % subsection)
17.1018 + self.stream.write(label)
17.1019 + self.stream.write("</div>\n")
17.1020 + self.stream.write("</div>\n")
17.1021 + self.stream.write("</span>\n")
17.1022 +
17.1023 + def _op(self, node):
17.1024 + if hasattr(node, "_left_call") and hasattr(node, "_right_call"):
17.1025 + return self._invocations(node._left_call.active() + node._right_call.active())
17.1026 + else:
17.1027 + _node = node._node
17.1028 + if isinstance(_node, Not):
17.1029 + _node = _node.expr
17.1030 + return self._invocations(_node.active())
17.1031 +
17.1032 + def _invocations(self, nodes):
17.1033 + invocations = []
17.1034 + for node in nodes:
17.1035 + if hasattr(node, "invocations"):
17.1036 + invocations += node.invocations
17.1037 +
17.1038 + # Record each link, avoiding duplicates.
17.1039 +
17.1040 + links = {}
17.1041 + for invocation in invocations:
17.1042 + fn = getattr(invocation, "copy_of", invocation).full_name()
17.1043 + module = invocation.module.name
17.1044 + name = invocation.name
17.1045 + structures = [x.name for x in invocation.structures]
17.1046 + qualified_name = ".".join([module] + structures + [name])
17.1047 +
17.1048 + # Record the label and the link texts.
17.1049 +
17.1050 + label = self._text(qualified_name)
17.1051 + link = (self._url(module), self._url(fn))
17.1052 + links[label] = link
17.1053 +
17.1054 + # Produce the list.
17.1055 +
17.1056 + if links:
17.1057 + popup_labels = []
17.1058 + for label, (module_name, target_name) in links.items():
17.1059 + popup_labels.append("<a href='%s%sxhtml#%s'>%s</a>" % (module_name, os.path.extsep, target_name, label))
17.1060 + else:
17.1061 + popup_labels = []
17.1062 +
17.1063 + if popup_labels:
17.1064 + return [("invocations", "invocation", popup_labels)]
17.1065 + else:
17.1066 + return []
17.1067 +
17.1068 + def _types(self, nodes):
17.1069 + all_types = [(getattr(n, "types", []) or flatten(getattr(n, "writes", {}).values())) for n in nodes]
17.1070 + types = flatten(all_types)
17.1071 + return self._types_container(types, "types")
17.1072 +
17.1073 + def _types_container(self, types, style_class):
17.1074 + labels = {}
17.1075 + for type in types:
17.1076 + fn = type.type.full_name()
17.1077 + labels[self._text(fn)] = None
17.1078 +
17.1079 + if labels:
17.1080 + return [(style_class, 'type', labels.keys())]
17.1081 + else:
17.1082 + return []
17.1083 +
17.1084 + def _raises(self, nodes):
17.1085 +
17.1086 + "Output the exception information for the given simplified 'nodes'."
17.1087 +
17.1088 + raises = set()
17.1089 + for node in nodes:
17.1090 + if hasattr(node, "raises") and node.raises:
17.1091 + raises.update(node.raises)
17.1092 + return self._types_container(raises, "raises")
17.1093 +
17.1094 + def _scopes(self, nodes):
17.1095 +
17.1096 + "Output the scope information for the given simplified 'nodes'."
17.1097 +
17.1098 + labels = {}
17.1099 + for node in nodes:
17.1100 +
17.1101 + # Straightforward name loading/storing involves the local scope.
17.1102 +
17.1103 + if isinstance(node, StoreName) or isinstance(node, LoadName):
17.1104 + labels["(local)"] = None
17.1105 +
17.1106 + # Other loading/storing involves attributes accessed on modules, classes
17.1107 + # and objects.
17.1108 +
17.1109 + else:
17.1110 +
17.1111 + # Loading...
17.1112 +
17.1113 + if hasattr(node, "accesses") and node.accesses:
17.1114 + for ref, accesses in node.accesses.items():
17.1115 + fn = ref.full_name()
17.1116 + for attr, access in accesses:
17.1117 + access_fn = access.full_name()
17.1118 + label = self._text(fn)
17.1119 + if ref != access:
17.1120 + label += " (via " + self._text(access_fn) + ")"
17.1121 + labels[label] = None
17.1122 +
17.1123 + # Storing...
17.1124 +
17.1125 + if hasattr(node, "writes") and node.writes:
17.1126 + for ref in node.writes.keys():
17.1127 + fn = ref.full_name()
17.1128 + labels[self._text(fn)] = None
17.1129 +
17.1130 + # Non-loading...
17.1131 +
17.1132 + if hasattr(node, "non_accesses") and node.non_accesses:
17.1133 + self._types_container(node.non_accesses, "non-accesses")
17.1134 +
17.1135 + # Non-storing...
17.1136 +
17.1137 + if hasattr(node, "non_writes") and node.non_writes:
17.1138 + self._types_container(node.non_writes, "non-writes")
17.1139 +
17.1140 + if labels:
17.1141 + return [("scopes", "scope", labels.keys())]
17.1142 + else:
17.1143 + return []
17.1144 +
17.1145 +# Utility functions.
17.1146 +
17.1147 +def flatten(lists):
17.1148 + result = set()
17.1149 + for l in lists:
17.1150 + result.update(l)
17.1151 + return result
17.1152 +
17.1153 +# Convenience functions.
17.1154 +
17.1155 +def browse(module, stream=None):
17.1156 + browser = Browser(stream or sys.stdout)
17.1157 + browser.process(module.original)
17.1158 +
17.1159 +def makedoc(module, filename):
17.1160 + stream = open(filename, "wb")
17.1161 + try:
17.1162 + browser = Browser(stream)
17.1163 + browser.process(module.original)
17.1164 + finally:
17.1165 + stream.close()
17.1166 +
17.1167 +def makedocs(module, modules, builtins):
17.1168 + dirname = "%s-docs" % module.name
17.1169 + if not os.path.exists(dirname):
17.1170 + os.mkdir(dirname)
17.1171 + for m in [module, builtins] + modules:
17.1172 + makedoc(m, os.path.join(dirname, "%s%sxhtml" % (m.name, os.path.extsep)))
17.1173 +
17.1174 +# vim: tabstop=4 expandtab shiftwidth=4
18.1 --- a/viewer.py Sun May 27 18:19:01 2007 +0200
18.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
18.3 @@ -1,1171 +0,0 @@
18.4 -#!/usr/bin/env python
18.5 -
18.6 -"""
18.7 -View annotated sources.
18.8 -
18.9 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
18.10 -
18.11 -This software is free software; you can redistribute it and/or
18.12 -modify it under the terms of the GNU General Public License as
18.13 -published by the Free Software Foundation; either version 2 of
18.14 -the License, or (at your option) any later version.
18.15 -
18.16 -This software is distributed in the hope that it will be useful,
18.17 -but WITHOUT ANY WARRANTY; without even the implied warranty of
18.18 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18.19 -GNU General Public License for more details.
18.20 -
18.21 -You should have received a copy of the GNU General Public
18.22 -License along with this library; see the file LICENCE.txt
18.23 -If not, write to the Free Software Foundation, Inc.,
18.24 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18.25 -"""
18.26 -
18.27 -from compiler.visitor import ASTVisitor
18.28 -from simplified import *
18.29 -import sys
18.30 -import os
18.31 -import textwrap
18.32 -
18.33 -# Classes.
18.34 -
18.35 -# HTML-related output production.
18.36 -
18.37 -html_header = """<?xml version="1.0" encoding="iso-8859-15"?>
18.38 -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
18.39 -<html xmlns="http://www.w3.org/1999/xhtml">
18.40 -<head>
18.41 - <title>Module</title>
18.42 - <style type="text/css">
18.43 - html {
18.44 - background-color: black; color: white;
18.45 - }
18.46 -
18.47 - body {
18.48 - padding-bottom: 4em;
18.49 - font-size: 14pt; font-family: monospace;
18.50 - background-color: black; color: white;
18.51 - }
18.52 -
18.53 - .class { margin-top: 1em; margin-bottom: 1em; }
18.54 - .function { margin-top: 1em; margin-bottom: 1em; }
18.55 - .body { padding-left: 2em; }
18.56 - .keyword { color: yellow; }
18.57 - .comment { color: blue; }
18.58 - .class-name { color: cyan; }
18.59 - .function-name { color: cyan; }
18.60 - .str { color: #FF00FF; }
18.61 - .doc { color: #FF00FF; margin-top: 1em; margin-bottom: 1em; }
18.62 - .invocation a { color: white; text-decoration: none; }
18.63 -
18.64 - .popup {
18.65 - display: none; z-index: 2;
18.66 - position: absolute; top: 2ex; left: 0;
18.67 - padding: 0.2em; background-color: #000000; color: white;
18.68 - border: 2px solid #dddddd;
18.69 - }
18.70 -
18.71 - .invocations {
18.72 - padding: 0.5em; background-color: #770000;
18.73 - clear: all;
18.74 - }
18.75 -
18.76 - .types {
18.77 - padding: 0.5em; background-color: #0000FF;
18.78 - float: right;
18.79 - }
18.80 -
18.81 - .raises {
18.82 - padding: 0.5em; background-color: #7700FF;
18.83 - float: right;
18.84 - }
18.85 -
18.86 - .scopes {
18.87 - padding: 0.5em; background-color: #007700;
18.88 - float: left;
18.89 - }
18.90 -
18.91 - .non-writes, .non-accesses {
18.92 - padding: 0.5em; background-color: #FF0000;
18.93 - float: right;
18.94 - }
18.95 -
18.96 - .op,
18.97 - .name,
18.98 - .attr,
18.99 - .conditional,
18.100 - .operator,
18.101 - .iterator,
18.102 - .call,
18.103 - .returns,
18.104 - .failure
18.105 - {
18.106 - position: relative;
18.107 - }
18.108 -
18.109 - .op:hover > .popup,
18.110 - .name:hover > .popup,
18.111 - .attr:hover > .popup,
18.112 - .conditional:hover > .popup,
18.113 - .operator:hover > .popup,
18.114 - .iterator:hover > .popup,
18.115 - .call:hover > .popup,
18.116 - .returns:hover > .popup,
18.117 - .failure:hover > .popup
18.118 - {
18.119 - display: block;
18.120 - }
18.121 -
18.122 - </style>
18.123 -</head>
18.124 -<body>
18.125 -"""
18.126 -
18.127 -html_footer = """</body>
18.128 -</html>
18.129 -"""
18.130 -
18.131 -# Browser classes.
18.132 -
18.133 -class Browser(ASTVisitor):
18.134 -
18.135 - """
18.136 - A browsing visitor for AST nodes.
18.137 -
18.138 - Covered: Add, And, Assert, AssAttr, AssList, AssName, AssTuple, Assign,
18.139 - AugAssign, Bitand, Break, CallFunc, Class, Compare, Const,
18.140 - Continue, Dict, Discard, Div, FloorDiv, For, From, Function,
18.141 - Getattr, Global, If, Import, Keyword, Lambda, List, ListComp,
18.142 - ListCompFor, ListCompIf, Mod, Module, Mul, Name, Not, Or, Pass,
18.143 - Power, Print, Printnl, Raise, Return, Slice, Sliceobj, Stmt, Sub,
18.144 - Subscript, TryExcept, TryFinally, Tuple, UnaryAdd, UnarySub, While.
18.145 -
18.146 - Missing: Backquote, Bitor, Bitxor, Decorators, Ellipsis,
18.147 - Exec, Invert, LeftShift, RightShift, Yield.
18.148 - """
18.149 -
18.150 - def __init__(self, stream):
18.151 - ASTVisitor.__init__(self)
18.152 - self.visitor = self
18.153 - self.stream = stream
18.154 -
18.155 - def process(self, module):
18.156 - self.stream.write(html_header)
18.157 - self.dispatch(module)
18.158 - self.stream.write(html_footer)
18.159 -
18.160 - def visitModule(self, node):
18.161 - self.default(node)
18.162 -
18.163 - # Statements.
18.164 -
18.165 - def visitAssert(self, node):
18.166 - self.stream.write("<div class='assert'>\n")
18.167 - self.stream.write("<span class='failure'>\n")
18.168 - self._keyword("assert")
18.169 - self._popup(
18.170 - self._types(node._raises.active())
18.171 - )
18.172 - self.stream.write("</span>\n")
18.173 - self.dispatch(node.test)
18.174 - if node.fail:
18.175 - self.stream.write(", ")
18.176 - self.dispatch(node.fail)
18.177 - self.stream.write("</div>\n")
18.178 -
18.179 - def visitAssign(self, node):
18.180 - self.stream.write("<div class='assign'>\n")
18.181 - for lvalue in node.nodes:
18.182 - self.dispatch(lvalue)
18.183 - self.stream.write("=\n")
18.184 - self.dispatch(node.expr)
18.185 - self.stream.write("</div>\n")
18.186 -
18.187 - def visitAugAssign(self, node):
18.188 - self.stream.write("<div class='augassign'>\n")
18.189 - self.dispatch(node.node)
18.190 - self.stream.write("<span class='operator'>\n")
18.191 - self.stream.write("%s\n" % node.op)
18.192 - self._popup(
18.193 - self._invocations(node._op_call.active())
18.194 - )
18.195 - self.stream.write("</span>\n")
18.196 - self.dispatch(node.expr)
18.197 - self.stream.write("</div>\n")
18.198 -
18.199 - def visitBreak(self, node):
18.200 - self.stream.write("<div class='break'>\n")
18.201 - self._keyword("break")
18.202 - self.stream.write("</div>\n")
18.203 -
18.204 - def visitClass(self, node):
18.205 - definition = node._node
18.206 - definitions = definition.active()
18.207 - structure = definition.expr.ref
18.208 - self.stream.write("<div class='class' id='%s'>\n" % structure.full_name())
18.209 - self.stream.write("<div>\n")
18.210 - self._keyword("class")
18.211 - self._name_start(structure.name, "class-name")
18.212 - self._popup(
18.213 - self._scopes(definitions)
18.214 - )
18.215 - self._name_end()
18.216 - bases = structure.bases
18.217 -
18.218 - # Suppress the "object" class appearing alone.
18.219 -
18.220 - if bases and not (len(bases) == 1 and bases[0].name == "object"):
18.221 - self.stream.write("(")
18.222 - first = 1
18.223 - for base in bases:
18.224 - if not first:
18.225 - self.stream.write(",\n")
18.226 - self._name_start(base.name)
18.227 - self._popup(
18.228 - self._scopes([base]) +
18.229 - self._types([base])
18.230 - )
18.231 - self._name_end()
18.232 - first = 0
18.233 - self.stream.write(")")
18.234 -
18.235 - self.stream.write(":\n")
18.236 - self._comment(self._text(structure.full_name()))
18.237 - self.stream.write("</div>\n")
18.238 -
18.239 - self.stream.write("<div class='body'>\n")
18.240 - self._doc(node)
18.241 - self.dispatch(node.code)
18.242 - self.stream.write("</div>\n")
18.243 - self.stream.write("</div>\n")
18.244 -
18.245 - def visitContinue(self, node):
18.246 - self.stream.write("<div class='continue'>\n")
18.247 - self._keyword("continue")
18.248 - self.stream.write("</div>\n")
18.249 -
18.250 - def visitDiscard(self, node):
18.251 - self.stream.write("<div class='discard'>\n")
18.252 - self.default(node)
18.253 - self.stream.write("</div>\n")
18.254 -
18.255 - def visitFor(self, node):
18.256 - self.stream.write("<div class='if'>\n")
18.257 - self.stream.write("<div>\n")
18.258 - self.stream.write("<span class='iterator'>\n")
18.259 - self._keyword("for")
18.260 - self._popup(
18.261 - self._invocations(node._next_call.active())
18.262 - )
18.263 - self.stream.write("</span>\n")
18.264 - self.dispatch(node.assign)
18.265 - self.stream.write("<span class='iterator'>\n")
18.266 - self._keyword("in")
18.267 - self._popup(
18.268 - self._invocations(node._iter_call.active())
18.269 - )
18.270 - self.stream.write("</span>\n")
18.271 - self.dispatch(node.list)
18.272 - self.stream.write(":\n")
18.273 - self.stream.write("</div>\n")
18.274 - self.stream.write("<div class='body'>\n")
18.275 - self.dispatch(node.body)
18.276 - self.stream.write("</div>\n")
18.277 - if node.else_ is not None:
18.278 - self.stream.write("<div>\n")
18.279 - self._keyword("else")
18.280 - self.stream.write(":\n")
18.281 - self.stream.write("</div>\n")
18.282 - self.stream.write("<div class='body'>\n")
18.283 - self.dispatch(node.else_)
18.284 - self.stream.write("</div>\n")
18.285 - self.stream.write("</div>\n")
18.286 -
18.287 - def visitFrom(self, node):
18.288 - self.stream.write("<div class='from'>\n")
18.289 - self._keyword("from")
18.290 - self.stream.write("<span class='name'>\n")
18.291 - self.stream.write(node.modname)
18.292 - self._popup(
18.293 - self._types(node._modname.active())
18.294 - )
18.295 - self.stream.write("</span>\n")
18.296 - self._keyword("import")
18.297 - first = 1
18.298 - for (name, alias), _name in map(None, node.names, node._names):
18.299 - if not first:
18.300 - self.stream.write(",\n")
18.301 - if alias:
18.302 - self.stream.write(name + " ")
18.303 - self._keyword("as")
18.304 - self.stream.write("<span class='name'>\n")
18.305 - self.stream.write(alias or name)
18.306 - self._popup(
18.307 - self._types([_name])
18.308 - )
18.309 - self.stream.write("</span>\n")
18.310 - first = 0
18.311 - self.stream.write("</div>\n")
18.312 -
18.313 - def visitFunction(self, node):
18.314 - definition = node._node
18.315 - definitions = [n for n in definition.active() if not isinstance(n, Subprogram)]
18.316 - subprogram = node._subprogram
18.317 - subprograms = subprogram.active()
18.318 - self.stream.write("<div class='function' id='%s'>\n" % subprogram.full_name())
18.319 - self.stream.write("<div>\n")
18.320 - self._keyword("def")
18.321 - self._name_start(subprogram.name, "function-name")
18.322 - self._popup(
18.323 - self._scopes([definition]) + # not dependent on subprograms
18.324 - self._raises(subprograms)
18.325 - )
18.326 - self._name_end()
18.327 - self.stream.write("(")
18.328 - self._parameters(subprogram, subprograms)
18.329 - self.stream.write(")")
18.330 - self.stream.write(":\n")
18.331 - self._comment(self._text(subprogram.full_name()))
18.332 - self.stream.write("</div>\n")
18.333 -
18.334 - self.stream.write("<div class='body'>\n")
18.335 - self._doc(node)
18.336 - self.dispatch(node.code)
18.337 - self.stream.write("</div>\n")
18.338 - self.stream.write("</div>\n")
18.339 -
18.340 - def visitGlobal(self, node):
18.341 - self.stream.write("<div class='global'>\n")
18.342 - self._keyword("global")
18.343 - first = 1
18.344 - for name in node.names:
18.345 - if not first:
18.346 - self.stream.write(",\n")
18.347 - self.stream.write(name)
18.348 - first = 0
18.349 - self.stream.write("</div>\n")
18.350 -
18.351 - def visitIf(self, node):
18.352 - self.stream.write("<div class='if'>\n")
18.353 - first = 1
18.354 - conditional = node._node
18.355 - conditionals = conditional.active()
18.356 - for compare, stmt in node.tests:
18.357 - self.stream.write("<div>\n")
18.358 - self.stream.write("<span class='conditional'>\n")
18.359 - if first:
18.360 - self._keyword("if")
18.361 - else:
18.362 - self._keyword("elif")
18.363 - self._popup(
18.364 - self._invocations([c.test for c in conditionals])
18.365 - )
18.366 - self.stream.write("</span>\n")
18.367 - self.dispatch(compare)
18.368 - self.stream.write(":\n")
18.369 - self.stream.write("</div>\n")
18.370 - self.stream.write("<div class='body'>\n")
18.371 - self.dispatch(stmt)
18.372 - self.stream.write("</div>\n")
18.373 - if conditional.else_:
18.374 - conditional = conditional.else_[0]
18.375 - conditionals = conditional.active()
18.376 - else:
18.377 - conditional = None
18.378 - conditionals = []
18.379 - first = 0
18.380 - if node.else_ is not None:
18.381 - self.stream.write("<div>\n")
18.382 - self._keyword("else")
18.383 - self.stream.write(":\n")
18.384 - self.stream.write("</div>\n")
18.385 - self.stream.write("<div class='body'>\n")
18.386 - self.dispatch(node.else_)
18.387 - self.stream.write("</div>\n")
18.388 - self.stream.write("</div>\n")
18.389 -
18.390 - def visitImport(self, node):
18.391 - self.stream.write("<div class='import'>\n")
18.392 - self._keyword("import")
18.393 - first = 1
18.394 - for (name, alias), _name in map(None, node.names, node._names):
18.395 - if not first:
18.396 - self.stream.write(",\n")
18.397 - if alias:
18.398 - self.stream.write(name + " ")
18.399 - self._keyword("as")
18.400 - self.stream.write("<span class='name'>\n")
18.401 - self.stream.write(alias or name)
18.402 - self._popup(
18.403 - self._types([_name])
18.404 - )
18.405 - self.stream.write("</span>\n")
18.406 - first = 0
18.407 - self.stream.write("</div>\n")
18.408 -
18.409 - def visitPass(self, node):
18.410 - self.stream.write("<div class='pass'>\n")
18.411 - self._keyword("pass")
18.412 - self.stream.write("</div>\n")
18.413 -
18.414 - def visitPrint(self, node):
18.415 - self.stream.write("<div class='print'>\n")
18.416 - self._keyword("print")
18.417 - if node.dest is not None:
18.418 - self.stream.write(">>\n")
18.419 - self.dispatch(node.dest)
18.420 - for n in node.nodes:
18.421 - self.dispatch(n)
18.422 - self.stream.write(",\n")
18.423 - self.stream.write("</div>\n")
18.424 -
18.425 - def visitPrintnl(self, node):
18.426 - self.stream.write("<div class='printnl'>\n")
18.427 - self._keyword("print")
18.428 - if node.dest is not None:
18.429 - self.stream.write(">>\n")
18.430 - self.dispatch(node.dest)
18.431 - first = 1
18.432 - for n in node.nodes:
18.433 - if not first:
18.434 - self.stream.write(",\n")
18.435 - self.dispatch(n)
18.436 - first = 0
18.437 - self.stream.write("</div>\n")
18.438 -
18.439 - def visitRaise(self, node):
18.440 - target = node._node.expr
18.441 - targets = target.active()
18.442 - self.stream.write("<div class='raise'>\n")
18.443 - self.stream.write("<span class='call'>\n")
18.444 - self._keyword("raise")
18.445 - self._popup(
18.446 - self._invocations(targets)
18.447 - )
18.448 - self.stream.write("</span>\n")
18.449 - self.dispatch(node.expr1)
18.450 - if node.expr2 is not None:
18.451 - self.stream.write(",\n")
18.452 - self.dispatch(node.expr2)
18.453 - if node.expr3 is not None:
18.454 - self.stream.write(",\n")
18.455 - self.dispatch(node.expr3)
18.456 - self.stream.write("</div>\n")
18.457 -
18.458 - def visitReturn(self, node):
18.459 - value = node._node
18.460 - values = value.active()
18.461 - self.stream.write("<div class='return'>\n")
18.462 - self.stream.write("<span class='returns'>\n")
18.463 - self._keyword("return")
18.464 - self._popup(
18.465 - self._types(values)
18.466 - )
18.467 - self.stream.write("</span>\n")
18.468 - self.dispatch(node.value)
18.469 - self.stream.write("</div>\n")
18.470 -
18.471 - def visitStmt(self, node):
18.472 - self.stream.write("<div class='stmt'>\n")
18.473 - self.default(node)
18.474 - self.stream.write("</div>\n")
18.475 -
18.476 - def visitTryExcept(self, node):
18.477 - self.stream.write("<div class='tryexcept'>\n")
18.478 - self.stream.write("<div>\n")
18.479 - self._keyword("try")
18.480 - self.stream.write(":\n")
18.481 - self.stream.write("</div>\n")
18.482 - self.stream.write("<div class='body'>\n")
18.483 - self.dispatch(node.body)
18.484 - self.stream.write("</div>\n")
18.485 - for spec, assign, statement in node.handlers:
18.486 - self.stream.write("<div>\n")
18.487 - self._keyword("except")
18.488 - if spec is not None:
18.489 - self.dispatch(spec)
18.490 - if assign is not None:
18.491 - self.stream.write(",\n")
18.492 - self.dispatch(assign)
18.493 - self.stream.write(":\n")
18.494 - self.stream.write("</div>\n")
18.495 - self.stream.write("<div class='body'>\n")
18.496 - self.dispatch(statement)
18.497 - self.stream.write("</div>\n")
18.498 - if node.else_ is not None:
18.499 - self.stream.write("<div>\n")
18.500 - self._keyword("else")
18.501 - self.stream.write(":\n")
18.502 - self.stream.write("</div>\n")
18.503 - self.stream.write("<div class='body'>\n")
18.504 - self.dispatch(node.else_)
18.505 - self.stream.write("</div>\n")
18.506 - self.stream.write("</div>\n")
18.507 -
18.508 - def visitTryFinally(self, node):
18.509 - self.stream.write("<div class='tryfinally'>\n")
18.510 - self.stream.write("<div>\n")
18.511 - self._keyword("try")
18.512 - self.stream.write(":\n")
18.513 - self.stream.write("</div>\n")
18.514 - self.stream.write("<div class='body'>\n")
18.515 - self.dispatch(node.body)
18.516 - self.stream.write("</div>\n")
18.517 - self.stream.write("<div>\n")
18.518 - self._keyword("finally")
18.519 - self.stream.write(":\n")
18.520 - self.stream.write("</div>\n")
18.521 - self.stream.write("<div class='body'>\n")
18.522 - self.dispatch(node.final)
18.523 - self.stream.write("</div>\n")
18.524 - self.stream.write("</div>\n")
18.525 -
18.526 - def visitWhile(self, node):
18.527 - self.stream.write("<div class='while'>\n")
18.528 - self.stream.write("<div>\n")
18.529 - self.stream.write("<span class='conditional'>\n")
18.530 - self._keyword("while")
18.531 - self._popup(
18.532 - self._invocations(node._test_call.active())
18.533 - )
18.534 - self.stream.write("</span>\n")
18.535 - self.dispatch(node.test)
18.536 - self.stream.write(":\n")
18.537 - self.stream.write("</div>\n")
18.538 - self.stream.write("<div class='body'>\n")
18.539 - self.dispatch(node.body)
18.540 - self.stream.write("</div>\n")
18.541 - if node.else_ is not None:
18.542 - self.stream.write("<div>\n")
18.543 - self._keyword("else")
18.544 - self.stream.write(":\n")
18.545 - self.stream.write("</div>\n")
18.546 - self.stream.write("<div class='body'>\n")
18.547 - self.dispatch(node.else_)
18.548 - self.stream.write("</div>\n")
18.549 - self.stream.write("</div>\n")
18.550 -
18.551 - # Expression-related helper methods.
18.552 -
18.553 - def _visitBinary(self, node, name, symbol):
18.554 - self.stream.write("<span class='%s'>\n" % name)
18.555 - self.dispatch(node.left)
18.556 - self.stream.write("<span class='operator'>\n")
18.557 - self.stream.write(self._text(symbol))
18.558 - self._popup(
18.559 - self._invocations(node._left_call.active() + node._right_call.active())
18.560 - )
18.561 - self.stream.write("</span>\n")
18.562 - self.dispatch(node.right)
18.563 - self.stream.write("</span>")
18.564 -
18.565 - def _visitUnary(self, node, name, symbol):
18.566 - self.stream.write("<span class='%s'>\n" % name)
18.567 - self.stream.write("<span class='operator'>\n")
18.568 - self.stream.write(symbol)
18.569 - self._popup(
18.570 - self._invocations(node._unary_call.active())
18.571 - )
18.572 - self.stream.write("</span>\n")
18.573 - self.dispatch(node.expr)
18.574 - self.stream.write("</span>")
18.575 -
18.576 - # Expressions.
18.577 -
18.578 - def visitAdd(self, node):
18.579 - self._visitBinary(node, "add", "+")
18.580 -
18.581 - def visitAnd(self, node):
18.582 - self.stream.write("<span class='and'>\n")
18.583 - first = 1
18.584 - for n in node.nodes:
18.585 - if not first:
18.586 - self._keyword("and")
18.587 - self.dispatch(n)
18.588 - first = 0
18.589 - self.stream.write("</span>")
18.590 -
18.591 - def visitAssAttr(self, node):
18.592 - target = node._node
18.593 - targets = target.active()
18.594 - self.stream.write("<span class='assattr'>\n")
18.595 - self.dispatch(node.expr)
18.596 - self.stream.write("<span class='attr'>\n")
18.597 - self.stream.write(".%s\n" % self._text(node.attrname))
18.598 - self._popup(
18.599 - self._scopes(targets) +
18.600 - self._types(targets)
18.601 - )
18.602 - self.stream.write("</span>\n")
18.603 - self.stream.write("</span>\n")
18.604 -
18.605 - def visitAssList(self, node):
18.606 - self.stream.write("<span class='list'>\n")
18.607 - self.stream.write("[")
18.608 - self._sequence(node)
18.609 - self.stream.write("]\n")
18.610 - self.stream.write("</span>\n")
18.611 -
18.612 - def visitAssName(self, node):
18.613 - target = node._node
18.614 - targets = target.active()
18.615 - self._name_start(target.name)
18.616 - self._popup(
18.617 - self._scopes(targets) +
18.618 - self._types(targets)
18.619 - )
18.620 - self._name_end()
18.621 -
18.622 - def visitAssTuple(self, node):
18.623 - self.stream.write("<span class='tuple'>\n")
18.624 - self.stream.write("(")
18.625 - self._sequence(node)
18.626 - self.stream.write(")\n")
18.627 - self.stream.write("</span>\n")
18.628 -
18.629 - def visitBitand(self, node):
18.630 - self.stream.write("<span class='bitand'>\n")
18.631 - self.dispatch(node.nodes[0])
18.632 - for op in node._ops:
18.633 - self.stream.write("<span class='op'>\n")
18.634 - self.stream.write(self._text(op.name))
18.635 - self._popup(
18.636 - self._op(op)
18.637 - )
18.638 - self.stream.write("</span>\n")
18.639 - self.dispatch(op.expr)
18.640 - self.stream.write("</span>")
18.641 -
18.642 - def visitCallFunc(self, node):
18.643 - target = node._node
18.644 - targets = target.active()
18.645 - self.stream.write("<span class='callfunc'>\n")
18.646 - self.dispatch(node.node)
18.647 - self.stream.write("<span class='call'>\n")
18.648 - self.stream.write("(")
18.649 - self._popup(
18.650 - self._invocations(targets)
18.651 - )
18.652 - self.stream.write("</span>\n")
18.653 - first = 1
18.654 - for arg in node.args:
18.655 - if not first:
18.656 - self.stream.write(",\n")
18.657 - self.dispatch(arg)
18.658 - first = 0
18.659 - if node.star_args is not None:
18.660 - if not first:
18.661 - self.stream.write(", *\n")
18.662 - self.dispatch(node.star_args)
18.663 - first = 0
18.664 - if node.dstar_args is not None:
18.665 - if not first:
18.666 - self.stream.write(", **\n")
18.667 - self.dispatch(node.dstar_args)
18.668 - first = 0
18.669 - self.stream.write(")\n")
18.670 - self.stream.write("</span>\n")
18.671 -
18.672 - def visitCompare(self, node):
18.673 - self.stream.write("<span class='compare'>\n")
18.674 - self.dispatch(node.expr)
18.675 - for op in node._ops:
18.676 - self.stream.write("<span class='op'>\n")
18.677 - self.stream.write(self._text(op.name))
18.678 - self._popup(
18.679 - self._op(op)
18.680 - )
18.681 - self.stream.write("</span>\n")
18.682 - self.dispatch(op.expr)
18.683 - self.stream.write("</span>\n")
18.684 -
18.685 - def visitConst(self, node):
18.686 - if isinstance(node.value, (str, unicode)):
18.687 - self.stream.write("<span class='str'>\n")
18.688 - self.stream.write(repr(node.value))
18.689 - if isinstance(node.value, (str, unicode)):
18.690 - self.stream.write("</span>\n")
18.691 -
18.692 - def visitDict(self, node):
18.693 - self.stream.write("<span class='dict'>\n")
18.694 - self.stream.write("{")
18.695 - self._mapping(node)
18.696 - self.stream.write("}\n")
18.697 - self.stream.write("</span>\n")
18.698 -
18.699 - def visitDiv(self, node):
18.700 - self._visitBinary(node, "div", "/")
18.701 -
18.702 - def visitFloorDiv(self, node):
18.703 - self._visitBinary(node, "floordiv", "//")
18.704 -
18.705 - def visitGetattr(self, node):
18.706 - target = node._node
18.707 - targets = target.active()
18.708 - self.stream.write("<span class='getattr'>\n")
18.709 - self.dispatch(node.expr)
18.710 - self.stream.write("<span class='attr'>\n")
18.711 - self.stream.write(".%s\n" % self._text(node.attrname))
18.712 - self._popup(
18.713 - self._scopes(targets) +
18.714 - self._types(targets)
18.715 - )
18.716 - self.stream.write("</span>\n")
18.717 - self.stream.write("</span>\n")
18.718 -
18.719 - def visitKeyword(self, node):
18.720 - self.stream.write("<span class='keyword-arg'>\n")
18.721 - self.stream.write(node.name)
18.722 - self.stream.write("=")
18.723 - self.dispatch(node.expr)
18.724 - self.stream.write("</span>\n")
18.725 -
18.726 - def visitLambda(self, node):
18.727 - definition = node._node
18.728 - definitions = [n for n in definition.active() if not isinstance(n, Subprogram)]
18.729 - subprogram = node._subprogram
18.730 - subprograms = subprogram.active()
18.731 - self.stream.write("<span class='lambda'>\n")
18.732 - self._keyword("lambda")
18.733 - self._parameters(subprogram, subprograms)
18.734 - self.dispatch(node.code)
18.735 - self.stream.write("</span>\n")
18.736 -
18.737 - visitList = visitAssList
18.738 -
18.739 - def visitListComp(self, node):
18.740 - self.stream.write("<span class='listcomp'>\n")
18.741 - self.stream.write("[")
18.742 - self.dispatch(node.expr)
18.743 - for qual in node.quals:
18.744 - self.dispatch(qual)
18.745 - self.stream.write("]\n")
18.746 - self.stream.write("</span>\n")
18.747 -
18.748 - def visitListCompFor(self, node):
18.749 - self.stream.write("<span class='listcompfor'>\n")
18.750 - self.stream.write("<span class='iterator'>\n")
18.751 - self._keyword("for")
18.752 - self._popup(
18.753 - self._invocations(node._next_call.active())
18.754 - )
18.755 - self.stream.write("</span>\n")
18.756 - self.dispatch(node.assign)
18.757 - self.stream.write("<span class='iterator'>\n")
18.758 - self._keyword("in")
18.759 - self._popup(
18.760 - self._invocations(node._iter_call.active())
18.761 - )
18.762 - self.stream.write("</span>\n")
18.763 - self.dispatch(node.list)
18.764 - for if_ in node.ifs:
18.765 - self.dispatch(if_)
18.766 - self.stream.write("</span>\n")
18.767 -
18.768 - def visitListCompIf(self, node):
18.769 - conditional = node._node
18.770 - conditionals = conditional.active()
18.771 - self.stream.write("<span class='listcompif'>\n")
18.772 - self.stream.write("<span class='conditional'>\n")
18.773 - self._keyword("if")
18.774 - self._popup(
18.775 - self._invocations([c.test for c in conditionals])
18.776 - )
18.777 - self.stream.write("</span>\n")
18.778 - self.dispatch(node.test)
18.779 - self.stream.write("</span>\n")
18.780 -
18.781 - def visitMod(self, node):
18.782 - self._visitBinary(node, "mod", "%")
18.783 -
18.784 - def visitMul(self, node):
18.785 - self._visitBinary(node, "mul", "*")
18.786 -
18.787 - def visitName(self, node):
18.788 - target = node._node
18.789 - targets = target.active()
18.790 - self._name_start(target.name)
18.791 - self._popup(
18.792 - self._scopes(targets) +
18.793 - self._types(targets)
18.794 - )
18.795 - self._name_end()
18.796 -
18.797 - def visitNot(self, node):
18.798 - self.stream.write("<span class='not'>\n")
18.799 - self._keyword("not")
18.800 - self.dispatch(node.expr)
18.801 - self.stream.write("</span>")
18.802 -
18.803 - def visitOr(self, node):
18.804 - self.stream.write("<span class='or'>\n")
18.805 - first = 1
18.806 - for n in node.nodes:
18.807 - if not first:
18.808 - self._keyword("or")
18.809 - self.dispatch(n)
18.810 - first = 0
18.811 - self.stream.write("</span>")
18.812 -
18.813 - def visitPower(self, node):
18.814 - self._visitBinary(node, "power", "**")
18.815 -
18.816 - def visitSlice(self, node):
18.817 - target = node._node
18.818 - targets = target.active()
18.819 - self.stream.write("<span class='slice'>\n")
18.820 - self.dispatch(node.expr)
18.821 - self.stream.write("<span class='call'>\n")
18.822 - self.stream.write("[")
18.823 - self._popup(
18.824 - self._invocations(targets)
18.825 - )
18.826 - self.stream.write("</span>\n")
18.827 - if node.lower:
18.828 - self.dispatch(node.lower)
18.829 - self.stream.write(":")
18.830 - if node.upper:
18.831 - self.dispatch(node.upper)
18.832 - # NOTE: Step?
18.833 - self.stream.write("]")
18.834 - self.stream.write("</span>\n")
18.835 -
18.836 - def visitSliceobj(self, node):
18.837 - self.stream.write("<span class='sliceobj'>\n")
18.838 - first = 1
18.839 - for n in node.nodes:
18.840 - if not first:
18.841 - self.stream.write(":")
18.842 - self.dispatch(n)
18.843 - self.stream.write("</span>\n")
18.844 -
18.845 - def visitSub(self, node):
18.846 - self._visitBinary(node, "sub", "-")
18.847 -
18.848 - def visitSubscript(self, node):
18.849 - target = node._node
18.850 - targets = target.active()
18.851 - self.stream.write("<span class='subscript'>\n")
18.852 - self.dispatch(node.expr)
18.853 - self.stream.write("<span class='call'>\n")
18.854 - self.stream.write("[")
18.855 - self._popup(
18.856 - self._invocations(targets)
18.857 - )
18.858 - self.stream.write("</span>\n")
18.859 - first = 1
18.860 - for sub in node.subs:
18.861 - if not first:
18.862 - self.stream.write(", ")
18.863 - self.dispatch(sub)
18.864 - first = 0
18.865 - self.stream.write("]")
18.866 - self.stream.write("</span>\n")
18.867 -
18.868 - visitTuple = visitAssTuple
18.869 -
18.870 - def visitUnaryAdd(self, node):
18.871 - self._visitUnary(node, "add", "+")
18.872 -
18.873 - def visitUnarySub(self, node):
18.874 - self._visitUnary(node, "sub", "-")
18.875 -
18.876 - # Output preparation methods.
18.877 -
18.878 - def _text(self, text):
18.879 - return text.replace("&", "&").replace("<", "<").replace(">", ">")
18.880 -
18.881 - def _attr(self, attr):
18.882 - return self._text(attr).replace("'", "'").replace('"', """)
18.883 -
18.884 - def _url(self, url):
18.885 - return self._attr(url).replace("#", "%23").replace("-", "%2d")
18.886 -
18.887 - def _comment(self, comment):
18.888 - self.stream.write("<span class='comment'># %s</span>\n" % comment)
18.889 -
18.890 - def _keyword(self, kw):
18.891 - self.stream.write("<span class='keyword'>%s</span> " % kw)
18.892 -
18.893 - def _doc(self, node):
18.894 - if node.doc is not None:
18.895 - self.stream.write("<pre class='doc'>\n")
18.896 - self.stream.write('"""')
18.897 - output = textwrap.dedent(node.doc.replace('"""', '\\"\\"\\"'))
18.898 - self.stream.write(self._text(output))
18.899 - self.stream.write('"""')
18.900 - self.stream.write("</pre>\n")
18.901 -
18.902 - def _sequence(self, node):
18.903 - first = 1
18.904 - for n in node.nodes:
18.905 - if not first:
18.906 - self.stream.write(",\n")
18.907 - self.dispatch(n)
18.908 - first = 0
18.909 -
18.910 - def _mapping(self, node):
18.911 - first = 1
18.912 - for k, v in node.items:
18.913 - if not first:
18.914 - self.stream.write(",\n")
18.915 - self.dispatch(k)
18.916 - self.stream.write(":\n")
18.917 - self.dispatch(v)
18.918 - first = 0
18.919 -
18.920 - def _parameters(self, subprogram, subprograms):
18.921 -
18.922 - # Get all the parameter lists.
18.923 -
18.924 - params = []
18.925 - nparams = 0
18.926 - for sub in subprograms:
18.927 - params.append(sub.params)
18.928 - nparams = max(nparams, len(sub.params))
18.929 - stars = []
18.930 - have_star = 0
18.931 - for sub in subprograms:
18.932 - stars.append(sub.star)
18.933 - if sub.star is not None:
18.934 - have_star = 1
18.935 - dstars = []
18.936 - have_dstar = 0
18.937 - for sub in subprograms:
18.938 - dstars.append(sub.dstar)
18.939 - if sub.dstar is not None:
18.940 - have_dstar = 1
18.941 -
18.942 - # Traverse the parameter lists, choosing a "column" at a time.
18.943 -
18.944 - first = 1
18.945 - for n in range(0, nparams):
18.946 - if not first:
18.947 - self.stream.write(",\n")
18.948 - main_param, main_default = subprogram.params[n]
18.949 - self._name_start(main_param)
18.950 - self._popup(
18.951 - self._parameter(subprograms, params, n)
18.952 - )
18.953 - self._name_end()
18.954 - self._default(main_default)
18.955 - first = 0
18.956 -
18.957 - if have_star:
18.958 - if not first:
18.959 - self.stream.write(", *\n")
18.960 - main_param, main_default = subprogram.star
18.961 - self._name_start(main_param)
18.962 - self._popup(
18.963 - self._parameter(subprograms, stars)
18.964 - )
18.965 - self._name_end()
18.966 - self._default(main_default)
18.967 - first = 0
18.968 -
18.969 - if have_dstar:
18.970 - if not first:
18.971 - self.stream.write(", **\n")
18.972 - main_param, main_default = subprogram.dstar
18.973 - self._name_start(main_param)
18.974 - self._popup(
18.975 - self._parameter(subprograms, dstars)
18.976 - )
18.977 - self._name_end()
18.978 - self._default(main_default)
18.979 - first = 0
18.980 -
18.981 - def _parameter(self, subprograms, params, n=None):
18.982 - types = set()
18.983 - for i in range(0, len(subprograms)):
18.984 - subprogram = subprograms[i]
18.985 - if n is not None:
18.986 - param, default = params[i][n]
18.987 - else:
18.988 - param, default = params[i]
18.989 - if hasattr(subprogram, "paramtypes"):
18.990 - types.update(subprogram.paramtypes[param])
18.991 - return self._types_container(types, "types")
18.992 -
18.993 - def _default(self, default):
18.994 - if default is not None and default.original is not None:
18.995 - self.stream.write("=\n")
18.996 - self.dispatch(default.original)
18.997 -
18.998 - def _name(self, name):
18.999 - self.stream.write("<span class='name'>%s</span>\n" % name)
18.1000 -
18.1001 - def _name_start(self, name, classes=None):
18.1002 - if classes is not None:
18.1003 - classes = " " + classes
18.1004 - else:
18.1005 - classes = ""
18.1006 - self.stream.write("<span class='name%s'>%s\n" % (classes, name))
18.1007 -
18.1008 - def _name_end(self):
18.1009 - self.stream.write("</span>\n")
18.1010 -
18.1011 - def _popup(self, info):
18.1012 - if info:
18.1013 - self.stream.write("<span class='popup'>\n")
18.1014 - for section, subsection, labels in info:
18.1015 - self.stream.write("<div class='%s'>\n" % section)
18.1016 - for label in labels:
18.1017 - self.stream.write("<div class='%s'>\n" % subsection)
18.1018 - self.stream.write(label)
18.1019 - self.stream.write("</div>\n")
18.1020 - self.stream.write("</div>\n")
18.1021 - self.stream.write("</span>\n")
18.1022 -
18.1023 - def _op(self, node):
18.1024 - if hasattr(node, "_left_call") and hasattr(node, "_right_call"):
18.1025 - return self._invocations(node._left_call.active() + node._right_call.active())
18.1026 - else:
18.1027 - _node = node._node
18.1028 - if isinstance(_node, Not):
18.1029 - _node = _node.expr
18.1030 - return self._invocations(_node.active())
18.1031 -
18.1032 - def _invocations(self, nodes):
18.1033 - invocations = []
18.1034 - for node in nodes:
18.1035 - if hasattr(node, "invocations"):
18.1036 - invocations += node.invocations
18.1037 -
18.1038 - # Record each link, avoiding duplicates.
18.1039 -
18.1040 - links = {}
18.1041 - for invocation in invocations:
18.1042 - fn = getattr(invocation, "copy_of", invocation).full_name()
18.1043 - module = invocation.module.name
18.1044 - name = invocation.name
18.1045 - structures = [x.name for x in invocation.structures]
18.1046 - qualified_name = ".".join([module] + structures + [name])
18.1047 -
18.1048 - # Record the label and the link texts.
18.1049 -
18.1050 - label = self._text(qualified_name)
18.1051 - link = (self._url(module), self._url(fn))
18.1052 - links[label] = link
18.1053 -
18.1054 - # Produce the list.
18.1055 -
18.1056 - if links:
18.1057 - popup_labels = []
18.1058 - for label, (module_name, target_name) in links.items():
18.1059 - popup_labels.append("<a href='%s%sxhtml#%s'>%s</a>" % (module_name, os.path.extsep, target_name, label))
18.1060 - else:
18.1061 - popup_labels = []
18.1062 -
18.1063 - if popup_labels:
18.1064 - return [("invocations", "invocation", popup_labels)]
18.1065 - else:
18.1066 - return []
18.1067 -
18.1068 - def _types(self, nodes):
18.1069 - all_types = [(getattr(n, "types", []) or flatten(getattr(n, "writes", {}).values())) for n in nodes]
18.1070 - types = flatten(all_types)
18.1071 - return self._types_container(types, "types")
18.1072 -
18.1073 - def _types_container(self, types, style_class):
18.1074 - labels = {}
18.1075 - for type in types:
18.1076 - fn = type.type.full_name()
18.1077 - labels[self._text(fn)] = None
18.1078 -
18.1079 - if labels:
18.1080 - return [(style_class, 'type', labels.keys())]
18.1081 - else:
18.1082 - return []
18.1083 -
18.1084 - def _raises(self, nodes):
18.1085 -
18.1086 - "Output the exception information for the given simplified 'nodes'."
18.1087 -
18.1088 - raises = set()
18.1089 - for node in nodes:
18.1090 - if hasattr(node, "raises") and node.raises:
18.1091 - raises.update(node.raises)
18.1092 - return self._types_container(raises, "raises")
18.1093 -
18.1094 - def _scopes(self, nodes):
18.1095 -
18.1096 - "Output the scope information for the given simplified 'nodes'."
18.1097 -
18.1098 - labels = {}
18.1099 - for node in nodes:
18.1100 -
18.1101 - # Straightforward name loading/storing involves the local scope.
18.1102 -
18.1103 - if isinstance(node, StoreName) or isinstance(node, LoadName):
18.1104 - labels["(local)"] = None
18.1105 -
18.1106 - # Other loading/storing involves attributes accessed on modules, classes
18.1107 - # and objects.
18.1108 -
18.1109 - else:
18.1110 -
18.1111 - # Loading...
18.1112 -
18.1113 - if hasattr(node, "accesses") and node.accesses:
18.1114 - for ref, accesses in node.accesses.items():
18.1115 - fn = ref.full_name()
18.1116 - for attr, access in accesses:
18.1117 - access_fn = access.full_name()
18.1118 - label = self._text(fn)
18.1119 - if ref != access:
18.1120 - label += " (via " + self._text(access_fn) + ")"
18.1121 - labels[label] = None
18.1122 -
18.1123 - # Storing...
18.1124 -
18.1125 - if hasattr(node, "writes") and node.writes:
18.1126 - for ref in node.writes.keys():
18.1127 - fn = ref.full_name()
18.1128 - labels[self._text(fn)] = None
18.1129 -
18.1130 - # Non-loading...
18.1131 -
18.1132 - if hasattr(node, "non_accesses") and node.non_accesses:
18.1133 - self._types_container(node.non_accesses, "non-accesses")
18.1134 -
18.1135 - # Non-storing...
18.1136 -
18.1137 - if hasattr(node, "non_writes") and node.non_writes:
18.1138 - self._types_container(node.non_writes, "non-writes")
18.1139 -
18.1140 - if labels:
18.1141 - return [("scopes", "scope", labels.keys())]
18.1142 - else:
18.1143 - return []
18.1144 -
18.1145 -# Utility functions.
18.1146 -
18.1147 -def flatten(lists):
18.1148 - result = set()
18.1149 - for l in lists:
18.1150 - result.update(l)
18.1151 - return result
18.1152 -
18.1153 -# Convenience functions.
18.1154 -
18.1155 -def browse(module, stream=None):
18.1156 - browser = Browser(stream or sys.stdout)
18.1157 - browser.process(module.original)
18.1158 -
18.1159 -def makedoc(module, filename):
18.1160 - stream = open(filename, "wb")
18.1161 - try:
18.1162 - browser = Browser(stream)
18.1163 - browser.process(module.original)
18.1164 - finally:
18.1165 - stream.close()
18.1166 -
18.1167 -def makedocs(module, modules, builtins):
18.1168 - dirname = "%s-docs" % module.name
18.1169 - if not os.path.exists(dirname):
18.1170 - os.mkdir(dirname)
18.1171 - for m in [module, builtins] + modules:
18.1172 - makedoc(m, os.path.join(dirname, "%s%sxhtml" % (m.name, os.path.extsep)))
18.1173 -
18.1174 -# vim: tabstop=4 expandtab shiftwidth=4