| @@ -149,66 +149,54 @@ class Transformer(_Decoratable): | |||
| return token | |||
| def merge_transformers(base_transformer=None, **kwargs): | |||
| """ | |||
| Paramaters: | |||
| :param base_transformer: Transformer that all other transformers will be added to. | |||
| :param \**kwargs: Key-value arguments providing the prefix for the methods of the transformer and the Transformers themselves. | |||
| def merge_transformers(base_transformer=None, **transformers_to_merge): | |||
| """Merge a collection of transformers into the base_transformer, each into its own 'namespace'. | |||
| When called, it will collect the methods from each transformer, and assign them to base_transformer, | |||
| with their name prefixed with the given keyword, as ``prefix__methodname`. | |||
| Compose a new transformer from a base and the in the `**kwargs` provided Transformer instances. | |||
| This function is especially useful for processing grammars that import other grammars, | |||
| thereby creating some of their rules in a 'namespace'. (i.e with a consitent name prefix) | |||
| In this case, the key for the transformer should match the name of the imported grammar. | |||
| The key should match the grammar file that the Transformer is supposed to manipulate. | |||
| Paramaters: | |||
| base_transformer (Transformer, optional): The transformer that all other transformers will be added to. | |||
| **transformers_to_merge: Keyword arguments, in the form of ``name_prefix = transformer``. | |||
| This method is meant to aid the composing of large transformers that | |||
| manipulate grammars that cross multiple lark files. | |||
| Raises: | |||
| AttributeError: In case of a name collision in the merged methods | |||
| Example: | |||
| ```python | |||
| class T1(Transformer): | |||
| def method_on_main_grammar(self, children): | |||
| # Do something | |||
| pass | |||
| def imported_grammar__method(self, children): | |||
| # Do something else | |||
| pass | |||
| class TMain(Transformer): | |||
| def method_on_main_grammar(self, children): | |||
| # Do something | |||
| pass | |||
| class TImportedGrammar(Transformer): | |||
| def method(self, children): | |||
| # Do something else | |||
| pass | |||
| regular_transformer = T1() | |||
| composed_transformer = merge_transformers(TMain(), | |||
| imported_grammar=TImportedGrammar()) | |||
| ``` | |||
| In the above code block `regular_transformer` and `composed_transformer` | |||
| should behave identically. | |||
| """ | |||
| infix = "__" | |||
| ```python | |||
| class TBase(Transformer): | |||
| def start(self, children): | |||
| return children[0] + 'bar' | |||
| class TImportedGrammar(Transformer): | |||
| def foo(self, children): | |||
| return "foo" | |||
| composed_transformer = merge_transformers(TBase(), imported=TImportedGrammar()) | |||
| t = Tree('start', [ Tree('imported__foo', []) ]) | |||
| assert composed_transformer.transform(t) == 'foobar' | |||
| ``` | |||
| """ | |||
| if base_transformer is None: | |||
| base_transformer = Transformer() | |||
| for prefix, transformer in kwargs.items(): | |||
| prefix += infix | |||
| for prefix, transformer in transformers_to_merge.items(): | |||
| for method_name in dir(transformer): | |||
| method = getattr(transformer, method_name) | |||
| if not callable(method): | |||
| continue | |||
| if method_name.startswith("_") or method_name == "transform": | |||
| continue | |||
| new_method_name = prefix + method_name | |||
| if prefix + method_name in dir(base_transformer): | |||
| raise AttributeError( | |||
| ("Method '{new_method_name}' already present in base " | |||
| "transformer").format(new_method_name=new_method_name)) | |||
| setattr(base_transformer, new_method_name, method) | |||
| prefixed_method = prefix + "__" + method_name | |||
| if hasattr(base_transformer, prefixed_method): | |||
| raise AttributeError("Cannot merge: method '%s' appears more than once" % prefixed_method) | |||
| setattr(base_transformer, prefixed_method, method) | |||
| return base_transformer | |||