| @@ -13,6 +13,7 @@ from collections import deque | |||
| from operator import attrgetter | |||
| from importlib import import_module | |||
| from .. visitors import Discard | |||
| from ..lexer import Token | |||
| from ..utils import logger | |||
| from ..tree import Tree | |||
| @@ -262,6 +263,103 @@ class ForestVisitor(object): | |||
| input_stack.append(next_node) | |||
| continue | |||
| 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): | |||
| """ | |||
| A visitor for prioritizing ambiguous parts of the Forest. | |||