# HG changeset patch # User Paul Boddie # Date 1490995607 -7200 # Node ID 7dd8e4815848572e59d6f724338c5b46c7560fa9 # Parent 059cb35b44212c42e49246cf6e4d8e020bce45e0 Introduced a multiple outcome result which can be used to provide a choice of return values to supply the initialiser and alias details. diff -r 059cb35b4421 -r 7dd8e4815848 inspector.py --- a/inspector.py Fri Mar 31 18:42:27 2017 +0200 +++ b/inspector.py Fri Mar 31 23:26:47 2017 +0200 @@ -27,7 +27,8 @@ from referencing import Reference from resolving import NameResolving from results import AccessRef, InstanceRef, InvocationRef, LiteralSequenceRef, \ - LocalNameRef, NameRef, ResolvedNameRef, VariableRef + LocalNameRef, MultipleRef, NameRef, ResolvedNameRef, \ + Result, VariableRef import compiler import sys @@ -800,7 +801,7 @@ "Process the given operator node 'n'." - self.process_operator_chain(n.nodes, self.process_structure_node) + return self.process_operator_chain(n.nodes) def process_name_node(self, n): @@ -915,20 +916,24 @@ self.in_invocation) return None - def process_operator_chain(self, nodes, fn): + def process_operator_chain(self, nodes): """ - Process the given chain of 'nodes', applying 'fn' to each node or item. + Process the given chain of 'nodes', processing each node or item. Each node starts a new conditional region, effectively making a deeply- nested collection of if-like statements. """ + results = [] + tracker = self.trackers[-1] for item in nodes: tracker.new_branchpoint() tracker.new_branch() - fn(item) + result = self.process_structure_node(item) + if result: + results.append(result) for item in nodes[:-1]: tracker.shelve_branch() @@ -939,6 +944,8 @@ tracker.shelve_branch() tracker.merge_branches() + return MultipleRef(results) + def process_try_node(self, n): """ @@ -1322,22 +1329,8 @@ "Return a suitable initialiser reference for 'value'." - # Includes LiteralSequenceRef, ResolvedNameRef... - - if isinstance(value, (NameRef, AccessRef, InstanceRef)): + if isinstance(value, Result): return value.reference() - - # In general, invocations do not produce known results. However, the - # name initialisers are resolved once a module has been inspected. - - elif isinstance(value, InvocationRef): - return value.reference() - - # Variable references are unknown results. - - elif isinstance(value, VariableRef): - return value.reference() - else: return value diff -r 059cb35b4421 -r 7dd8e4815848 resolving.py --- a/resolving.py Fri Mar 31 18:42:27 2017 +0200 +++ b/resolving.py Fri Mar 31 23:26:47 2017 +0200 @@ -21,7 +21,7 @@ from common import init_item from results import AccessRef, InstanceRef, InvocationRef, LocalNameRef, \ - NameRef, ResolvedNameRef + MultipleRef, NameRef, ResolvedNameRef from referencing import Reference import sys @@ -283,12 +283,40 @@ initialised reference, along with any aliased name information. """ - const_accesses = self.const_accesses.get(path) - initialised_ref = None aliased_names = None no_reference = None, None + # Attempt to obtain a coherent reference from multiple outcomes. + + if isinstance(name_ref, MultipleRef): + refs = set() + aliases = [] + + for result in name_ref.results: + _initialised_ref, _aliased_names = self.resolve_reference(path, result) + + # Unsuitable references at any level cause the result to yield + # no reference. + + if not _initialised_ref: + refs = None + elif refs is not None: + refs.add(_initialised_ref) + + if not _aliased_names: + aliases = None + elif aliases is not None: + aliases += _aliased_names + + # Only unambiguous references are returned as initialising + # references. + + if refs and len(refs) == 1: + return list(refs)[0], aliases + else: + return None, aliases + # Unwrap invocations. if isinstance(name_ref, InvocationRef): @@ -297,6 +325,8 @@ else: invocation = False + const_accesses = self.const_accesses.get(path) + # Obtain a usable reference from names or constants. if isinstance(name_ref, ResolvedNameRef): diff -r 059cb35b4421 -r 7dd8e4815848 results.py --- a/results.py Fri Mar 31 18:42:27 2017 +0200 +++ b/results.py Fri Mar 31 23:26:47 2017 +0200 @@ -209,6 +209,26 @@ def __repr__(self): return "LiteralSequenceRef(%r, %r, %r, %r)" % (self.name, self.ref, self.node, self.items) +class MultipleRef(Result): + + "A multiple outcome result." + + def __init__(self, results): + self.results = results + + def reference(self): + refs = set(self.references()) + ref = len(refs) == 1 and list(refs)[0] or Reference("") + + def references(self): + refs = [] + for result in self.results: + refs.append(result.reference()) + return refs + + def __repr__(self): + return "MultipleRef(%r)" % self.results + class VariableRef(Result): "A variable reference."