| 
				
				
					
				
				
				 | 
			
			 | 
			@@ -12,10 +12,13 @@ from math import isinf | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from collections import deque | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from operator import attrgetter | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from importlib import import_module | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from functools import partial | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from ..parse_tree_builder import AmbiguousIntermediateExpander | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from ..visitors import Discard | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from ..lexer import Token | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from ..utils import logger | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from ..tree import Tree | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from ..exceptions import ParseError | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class ForestNode(object): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    pass | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -125,6 +128,10 @@ class PackedNode(ForestNode): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return self.is_empty, -self.priority, self.rule.order | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    @property | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def children(self): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return [x for x in [self.left, self.right] if x is not None] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __iter__(self): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return iter([self.left, self.right]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -153,7 +160,12 @@ class ForestVisitor(object): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Use this as a base when you need to walk the forest. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    __slots__ = ['result'] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def get_cycle_in_path(self, node, path): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        index = len(path) - 1 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        while id(path[index]) != id(node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            index -= 1 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return path[index:] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_token_node(self, node): pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_symbol_node_in(self, node): pass | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -161,8 +173,18 @@ class ForestVisitor(object): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_in(self, node): pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_out(self, node): pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def on_cycle(self, node, path): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        """Called when a cycle is encountered. `node` is the node that causes | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        the cycle. `path` the list of nodes being visited: nodes that have been | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        entered but not exited. The first element is the root in a forest | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        visit, and the last element is the node visited most recently. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        `path` should be treated as read-only. The utility function | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        `get_cycle_in_path` may be used to obtain a slice of `path` that only | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        contains the nodes that make up the cycle.""" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit(self, root): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.result = None | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # Visiting is a list of IDs of all symbol/intermediate nodes currently in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # the stack. It serves two purposes: to detect when we 'recurse' in and out | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # of a symbol/intermediate so that we can process both up and down. Also, | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -170,6 +192,10 @@ class ForestVisitor(object): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # to recurse into a node that's already on the stack (infinite recursion). | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        visiting = set() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # a list of nodes that are currently being visited | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # used for the `on_cycle` callback | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        path = list() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # We do not use recursion here to walk the Forest due to the limited | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # stack size in python. Therefore input_stack is essentially our stack. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        input_stack = deque([root]) | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -180,7 +206,11 @@ class ForestVisitor(object): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        vpni = getattr(self, 'visit_packed_node_in') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        vsno = getattr(self, 'visit_symbol_node_out') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        vsni = getattr(self, 'visit_symbol_node_in') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        vino = getattr(self, 'visit_intermediate_node_out', vsno) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        vini = getattr(self, 'visit_intermediate_node_in', vsni) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        vtn = getattr(self, 'visit_token_node') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        oc = getattr(self, 'on_cycle') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        while input_stack: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            current = next(reversed(input_stack)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            try: | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -196,7 +226,8 @@ class ForestVisitor(object): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    continue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if id(next_node) in visiting: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    raise ParseError("Infinite recursion in grammar, in rule '%s'!" % next_node.s.name) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    oc(next_node, path) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    continue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                input_stack.append(next_node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                continue | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -208,25 +239,134 @@ class ForestVisitor(object): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            current_id = id(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if current_id in visiting: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if isinstance(current, PackedNode):    vpno(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                else:                                  vsno(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if isinstance(current, PackedNode): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    vpno(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                elif current.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    vino(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    vsno(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                input_stack.pop() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                path.pop() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                visiting.remove(current_id) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                continue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                visiting.add(current_id) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if isinstance(current, PackedNode): next_node = vpni(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                else:                               next_node = vsni(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                path.append(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if isinstance(current, PackedNode): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    next_node = vpni(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                elif current.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    next_node = vini(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    next_node = vsni(current) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if next_node is None: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    continue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if id(next_node) in visiting: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    raise ParseError("Infinite recursion in grammar!") | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if not isinstance(next_node, ForestNode) and \ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        not isinstance(next_node, Token): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    next_node = iter(next_node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                elif id(next_node) in visiting: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    oc(next_node, path) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    continue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                input_stack.append(next_node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                continue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return self.result | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class ForestTransformer(ForestVisitor): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """The base class for a bottom-up forest transformation. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Transformations are applied via inheritance and overriding of the | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    following methods: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    transform_symbol_node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    transform_intermediate_node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    transform_packed_node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    transform_token_node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    `transform_token_node` receives a Token as an argument. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    All other methods receive the node that is being transformed and | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    a list of the results of the transformations of that node's children. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    The return value of these methods are the resulting transformations. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    If `Discard` is raised in a transformation, no data from that node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    will be passed to its parent's transformation. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __init__(self): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # results of transformations | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.data = dict() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # used to track parent nodes | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.node_stack = deque() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def transform(self, root): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        """Perform a transformation on a Forest.""" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.node_stack.append('result') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.data['result'] = [] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.visit(root) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        assert len(self.data['result']) <= 1 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if self.data['result']: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return self.data['result'][0] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def transform_symbol_node(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def transform_intermediate_node(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def transform_packed_node(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def transform_token_node(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_symbol_node_in(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.node_stack.append(id(node)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.data[id(node)] = [] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return node.children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_in(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.node_stack.append(id(node)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.data[id(node)] = [] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return node.children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_token_node(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        try: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            transformed = self.transform_token_node(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        except Discard: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.data[self.node_stack[-1]].append(transformed) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_symbol_node_out(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.node_stack.pop() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        try: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            transformed = self.transform_symbol_node(node, self.data[id(node)]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        except Discard: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.data[self.node_stack[-1]].append(transformed) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        finally: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            del self.data[id(node)] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_intermediate_node_out(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.node_stack.pop() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        try: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            transformed = self.transform_intermediate_node(node, self.data[id(node)]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        except Discard: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.data[self.node_stack[-1]].append(transformed) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        finally: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            del self.data[id(node)] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_out(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.node_stack.pop() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        try: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            transformed = self.transform_packed_node(node, self.data[id(node)]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        except Discard: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.data[self.node_stack[-1]].append(transformed) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        finally: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            del self.data[id(node)] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class ForestSumVisitor(ForestVisitor): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -259,178 +399,203 @@ class ForestSumVisitor(ForestVisitor): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_symbol_node_out(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        node.priority = max(child.priority for child in node.children) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class ForestToTreeVisitor(ForestVisitor): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class PackedData(): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """Used in transformationss of packed nodes to distinguish the data | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    that comes from the left child and the right child. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    A Forest visitor which converts an SPPF forest to an unambiguous AST. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    The implementation in this visitor walks only the first ambiguous child | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    of each symbol node. When it finds an ambiguous symbol node it first | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    calls the forest_sum_visitor implementation to sort the children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    into preference order using the algorithms defined there; so the first | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    child should always be the highest preference. The forest_sum_visitor | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    implementation should be another ForestVisitor which sorts the children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    according to some priority mechanism. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __init__(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.left = None | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.right = None | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if data: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if node.left: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.left = data[0] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if len(data) > 1 and node.right: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    self.right = data[1] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            elif node.right: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.right = data[0] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class ForestToParseTree(ForestTransformer): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """Used by the earley parser when ambiguity equals 'resolve' or | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    'explicit'. Transforms an SPPF into an (ambiguous) parse tree. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    tree_class: The Tree class to use for construction | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    callbacks: A dictionary of rules to functions that output a tree | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    prioritizer: A ForestVisitor that manipulates the priorities of | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        ForestNodes | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    resolve_ambiguity: If True, ambiguities will be resolved based on | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        priorities. Otherwise, `_ambig` nodes will be in the resulting | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        tree. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    __slots__ = ['forest_sum_visitor', 'callbacks', 'output_stack'] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __init__(self, callbacks, forest_sum_visitor = None): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        assert callbacks | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.forest_sum_visitor = forest_sum_visitor | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __init__(self, tree_class=Tree, callbacks=dict(), prioritizer=ForestSumVisitor(), resolve_ambiguity=True): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        super(ForestToParseTree, self).__init__() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.tree_class = tree_class | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.callbacks = callbacks | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.prioritizer = prioritizer | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.resolve_ambiguity = resolve_ambiguity | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self._on_cycle_retreat = False | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def on_cycle(self, node, path): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        logger.warning("Cycle encountered in the SPPF at node: %s. " | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                "As infinite ambiguities cannot be represented in a tree, " | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                "this family of derivations will be discarded.", node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if self.resolve_ambiguity: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            # TODO: choose a different path if cycle is encountered | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            logger.warning("At this time, using ambiguity resolution for SPPFs " | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    "with cycles may result in None being returned.") | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self._on_cycle_retreat = True | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def _check_cycle(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if self._on_cycle_retreat: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            raise Discard | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit(self, root): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.output_stack = deque() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return super(ForestToTreeVisitor, self).visit(root) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def _collapse_ambig(self, children): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        new_children = [] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        for child in children: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if hasattr(child, 'data') and child.data == '_ambig': | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                new_children += child.children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                new_children.append(child) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return new_children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_token_node(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.output_stack[-1].append(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def _call_rule_func(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # called when transforming children of symbol nodes | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # data is a list of trees or tokens that correspond to the | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # symbol's rule expansion | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return self.callbacks[node.rule](data) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def _call_ambig_func(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # called when transforming a symbol node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # data is a list of trees where each tree's data is | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # equal to the name of the symbol or one of its aliases. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if len(data) > 1: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return self.tree_class('_ambig', data) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        elif data: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return data[0] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        raise Discard | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def transform_symbol_node(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self._check_cycle(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        data = self._collapse_ambig(data) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return self._call_ambig_func(node, data) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def transform_intermediate_node(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self._check_cycle(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if len(data) > 1: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            children = [self.tree_class('_inter', c) for c in data] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return self.tree_class('_iambig', children) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return data[0] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def transform_packed_node(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self._check_cycle(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        children = list() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        assert len(data) <= 2 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        data = PackedData(node, data) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if data.left is not None: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if node.left.is_intermediate and isinstance(data.left, list): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                children += data.left | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                children.append(data.left) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if data.right is not None: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            children.append(data.right) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if node.parent.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return self._call_rule_func(node, children) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_symbol_node_in(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if self.forest_sum_visitor and node.is_ambiguous and isinf(node.priority): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.forest_sum_visitor.visit(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return next(iter(node.children)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self._on_cycle_retreat = False | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        super(ForestToParseTree, self).visit_symbol_node_in(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if self.prioritizer and node.is_ambiguous and isinf(node.priority): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.prioritizer.visit(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if self.resolve_ambiguity: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return node.children[0] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return node.children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_in(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if not node.parent.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.output_stack.append([]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return iter([node.left, node.right]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_out(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if not node.parent.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            result = self.callbacks[node.rule](self.output_stack.pop()) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if self.output_stack: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.output_stack[-1].append(result) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.result = result | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class ForestToAmbiguousTreeVisitor(ForestToTreeVisitor): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    A Forest visitor which converts an SPPF forest to an ambiguous AST. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Because of the fundamental disparity between what can be stored in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    an SPPF and what can be stored in a Tree; this implementation is not | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    complete. It correctly deals with ambiguities that occur on symbol nodes only, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    and cannot deal with ambiguities that occur on intermediate nodes. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Usually, most parsers can be rewritten to avoid intermediate node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    ambiguities. Also, this implementation could be fixed, however | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    the code to handle intermediate node ambiguities is messy and | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    would not be performant. It is much better not to use this and | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    instead to correctly disambiguate the forest and only store unambiguous | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    parses in Trees. It is here just to provide some parity with the | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    old ambiguity='explicit'. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    This is mainly used by the test framework, to make it simpler to write | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    tests ensuring the SPPF contains the right results. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __init__(self, callbacks, forest_sum_visitor = ForestSumVisitor): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        super(ForestToAmbiguousTreeVisitor, self).__init__(callbacks, forest_sum_visitor) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self._on_cycle_retreat = False | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return super(ForestToParseTree, self).visit_packed_node_in(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_token_node(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.output_stack[-1].children.append(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_symbol_node_in(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if node.is_ambiguous: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if self.forest_sum_visitor and isinf(node.priority): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.forest_sum_visitor.visit(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if node.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                # TODO Support ambiguous intermediate nodes! | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                logger.warning("Ambiguous intermediate node in the SPPF: %s. " | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        "Lark does not currently process these ambiguities; resolving with the first derivation.", node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                return next(iter(node.children)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.output_stack.append(Tree('_ambig', [])) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self._on_cycle_retreat = False | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return super(ForestToParseTree, self).visit_token_node(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return iter(node.children) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			def handles_ambiguity(func): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """Decorator for methods of subclasses of TreeForestTransformer. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Denotes that the method should receive a list of transformed derivations.""" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    func.handles_ambiguity = True | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return func | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_symbol_node_out(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if not node.is_intermediate and node.is_ambiguous: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            result = self.output_stack.pop() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if self.output_stack: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.output_stack[-1].children.append(result) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.result = result | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class TreeForestTransformer(ForestToParseTree): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """A ForestTransformer with a tree-Transformer-like interface. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    By default, it will construct a tree. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_in(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if not node.parent.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.output_stack.append(Tree('drv', [])) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return iter([node.left, node.right]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Methods provided via inheritance are called based on the rule/symbol | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    names of nodes in the forest. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_out(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if not node.parent.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            result = self.callbacks[node.rule](self.output_stack.pop().children) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if self.output_stack: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.output_stack[-1].children.append(result) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.result = result | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class CompleteForestToAmbiguousTreeVisitor(ForestToTreeVisitor): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    An augmented version of ForestToAmbiguousTreeVisitor that is designed to | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    handle ambiguous intermediate nodes as well as ambiguous symbol nodes. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Methods that act on rules will receive a list of the results of the | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    transformations of the rule's children. By default, trees and tokens. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    On the way down: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Methods that act on tokens will receive a Token. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    - When an ambiguous intermediate node is encountered, an '_iambig' node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      is inserted into the tree. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    - Each possible derivation of an ambiguous intermediate node is represented | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      by an '_inter' node added as a child of the corresponding '_iambig' node. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Alternatively, methods that act on rules may be annotated with | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    `handles_ambiguity`. In this case, the function will receive a list | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    of all the transformations of all the derivations of the rule. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    By default, a list of trees where each tree.data is equal to the | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    rule name or one of its aliases. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    On the way up, these nodes are propagated up the tree and collapsed | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    into a single '_ambig' node for the nearest symbol node ancestor. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    This is achieved by the AmbiguousIntermediateExpander contained in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    the callbacks. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Non-tree transformations are made possible by override of | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    `__default__`, `__default_token__`, and `__default_ambig__`. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def _collapse_ambig(self, children): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        new_children = [] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        for child in children: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if child.data == '_ambig': | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                new_children += child.children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                new_children.append(child) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return new_children | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __init__(self, tree_class=Tree, prioritizer=ForestSumVisitor(), resolve_ambiguity=True): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        super(TreeForestTransformer, self).__init__(tree_class, dict(), prioritizer, resolve_ambiguity) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_token_node(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        self.output_stack[-1].children.append(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __default__(self, name, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        """Default operation on tree (for override). | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_symbol_node_in(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if node.is_ambiguous: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if self.forest_sum_visitor and isinf(node.priority): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.forest_sum_visitor.visit(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if node.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.output_stack.append(Tree('_iambig', [])) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.output_stack.append(Tree('_ambig', [])) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return iter(node.children) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        Returns a tree with name with data as children. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return self.tree_class(name, data) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_symbol_node_out(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if node.is_ambiguous: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            result = self.output_stack.pop() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if not node.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                result = Tree('_ambig', self._collapse_ambig(result.children)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if self.output_stack: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.output_stack[-1].children.append(result) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                self.result = result | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __default_ambig__(self, name, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        """Default operation on ambiguous rule (for override). | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_in(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if not node.parent.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.output_stack.append(Tree('drv', [])) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        elif node.parent.is_ambiguous: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.output_stack.append(Tree('_inter', [])) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return iter([node.left, node.right]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        Wraps data in an '_ambig_ node if it contains more than | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        one element.' | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if len(data) > 1: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return self.tree_class('_ambig', data) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        elif data: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return data[0] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        raise Discard | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def visit_packed_node_out(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if not node.parent.is_intermediate: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            result = self.callbacks[node.rule](self.output_stack.pop().children) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        elif node.parent.is_ambiguous: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            result = self.output_stack.pop() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if self.output_stack: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.output_stack[-1].children.append(result) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        else: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            self.result = result | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def __default_token__(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        """Default operation on Token (for override). | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        Returns node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return node | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def transform_token_node(self, node): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return getattr(self, node.type, self.__default_token__)(node) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def _call_rule_func(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        name = node.rule.alias or node.rule.options.template_source or node.rule.origin.name | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        user_func = getattr(self, name, self.__default__) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if user_func == self.__default__ or hasattr(user_func, 'handles_ambiguity'): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            user_func = partial(self.__default__, name) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if not self.resolve_ambiguity: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            wrapper = partial(AmbiguousIntermediateExpander, self.tree_class) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            user_func = wrapper(user_func) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return user_func(data) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    def _call_ambig_func(self, node, data): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        name = node.s.name | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        user_func = getattr(self, name, self.__default_ambig__) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if user_func == self.__default_ambig__ or not hasattr(user_func, 'handles_ambiguity'): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            user_func = partial(self.__default_ambig__, name) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return user_func(data) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class ForestToPyDotVisitor(ForestVisitor): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
	
		
			
				| 
				
					
				
				
				
				 | 
			
			 | 
			
  |