From fc24666f37304954a7508fd77638bbb529deaba6 Mon Sep 17 00:00:00 2001 From: "Robin A. Dorstijn" Date: Fri, 27 Aug 2021 09:45:00 +0200 Subject: [PATCH 1/6] Add merge function --- lark/visitors.py | 56 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_trees.py | 56 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/lark/visitors.py b/lark/visitors.py index 23ef64a..4894cab 100644 --- a/lark/visitors.py +++ b/lark/visitors.py @@ -149,6 +149,62 @@ class Transformer(_Decoratable): return token +def merge_transformers(base_transformer=None, **kwargs): + """ + Add the methods of other transformer to this one. + + This method is meant to aid in the maintenance of imports. + + 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. + """ + + if base_transformer is None: + base_transformer = Transformer() + for prefix, transformer in kwargs.items(): + prefix += "__" + + 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) + + return base_transformer + + class InlineTransformer(Transformer): # XXX Deprecated def _call_userfunc(self, tree, new_children=None): # Assumes tree is already transformed diff --git a/tests/test_trees.py b/tests/test_trees.py index c7f9787..730b80b 100644 --- a/tests/test_trees.py +++ b/tests/test_trees.py @@ -9,7 +9,7 @@ import functools from lark.tree import Tree from lark.lexer import Token from lark.visitors import Visitor, Visitor_Recursive, Transformer, Interpreter, visit_children_decor, v_args, Discard, Transformer_InPlace, \ - Transformer_InPlaceRecursive, Transformer_NonRecursive + Transformer_InPlaceRecursive, Transformer_NonRecursive, merge_transformers class TestTrees(TestCase): @@ -233,21 +233,69 @@ class TestTrees(TestCase): x = MyTransformer().transform( t ) self.assertEqual(x, t2) - + def test_transformer_variants(self): tree = Tree('start', [Tree('add', [Token('N', '1'), Token('N', '2')]), Tree('add', [Token('N', '3'), Token('N', '4')])]) for base in (Transformer, Transformer_InPlace, Transformer_NonRecursive, Transformer_InPlaceRecursive): class T(base): def add(self, children): return sum(children) - + def N(self, token): return int(token) - + copied = copy.deepcopy(tree) result = T().transform(copied) self.assertEqual(result, Tree('start', [3, 7])) + def test_merge_transformers(self): + tree = Tree('start', [ + Tree('main', [ + Token("A", '1'), Token("B", '2') + ]), + Tree("module__main", [ + Token("A", "2"), Token("B", "3") + ]) + ]) + + class T1(Transformer): + A = int + B = int + main = sum + start = list + def module__main(self, children): + prod = 1 + for child in children: + prod *= child + return prod + + class T2(Transformer): + A = int + B = int + main = sum + start = list + + class T3(Transformer): + def main(self, children): + prod = 1 + for child in children: + prod *= child + return prod + + class T4(Transformer): + def other_aspect(self, children): + pass + + t1_res = T1().transform(tree) + composed_res = merge_transformers(T2(), module=T3()).transform(tree) + self.assertEqual(t1_res, composed_res) + with self.assertRaises(AttributeError): + merge_transformers(T1(), module=T3()) + + try: + composed = merge_transformers(T1(), module=T4()) + except AttributeError: + self.fail("Should be able to add classes that do not conflict") if __name__ == '__main__': unittest.main() From fb8800914bf870fdb8bbf76afd109228400db120 Mon Sep 17 00:00:00 2001 From: "Robin A. Dorstijn" Date: Fri, 27 Aug 2021 17:07:55 +0200 Subject: [PATCH 2/6] Added advanced example for merge_transformers --- examples/advanced/advanced_transformers.py | 67 ++++++++++++++++++++++ examples/advanced/csv.lark | 14 +++++ examples/advanced/json.lark | 19 ++++++ examples/advanced/storage.lark | 8 +++ lark/visitors.py | 14 ++++- 5 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 examples/advanced/advanced_transformers.py create mode 100644 examples/advanced/csv.lark create mode 100644 examples/advanced/json.lark create mode 100644 examples/advanced/storage.lark diff --git a/examples/advanced/advanced_transformers.py b/examples/advanced/advanced_transformers.py new file mode 100644 index 0000000..9810f44 --- /dev/null +++ b/examples/advanced/advanced_transformers.py @@ -0,0 +1,67 @@ +""" +Transformer merging +================== + +This example is intended to show how transformers can be merged in order to +keep the individual steps clean and simple. + +.. note:: + The imported rules will have to be aliased according to the file it is in. + (See `storage.lark` for an implementation of this idea.) +""" +from lark import Lark, Tree +from json import dumps +from lark.visitors import Transformer, merge_transformers, v_args + +class JsonTreeToJson(Transformer): + @v_args(inline=True) + def string(self, s): + return s[1:-1].replace('\\"', '"') + + array = list + pair = tuple + object = dict + number = v_args(inline=True)(float) + + null = lambda self, _: None + true = lambda self, _: True + false = lambda self, _: False + +class CsvTreeToPandasDict(Transformer): + INT = int + FLOAT = float + SIGNED_FLOAT = float + WORD = str + NON_SEPARATOR_STRING = str + + def row(self, children): + return children + + def start(self, children): + data = {} + + header = children[0].children + for heading in header: + data[heading] = [] + + for row in children[1:]: + for i, element in enumerate(row): + data[header[i]].append(element) + + return data + +class Base(Transformer): + def start(self, children): + return children[0] + +if __name__ == "__main__": + merged = merge_transformers(Base(), csv=CsvTreeToPandasDict(), json=JsonTreeToJson()) + parser = Lark.open("storage.lark") + csv_tree = parser.parse("""# file lines author +data.json 12 Robin +data.csv 30 erezsh +compiler.py 123123 Megalng +""") + print("CSV data in pandas form:", merged.transform(csv_tree)) + json_tree = parser.parse(dumps({"test": "a", "dict": { "list": [1, 1.2] }})) + print("JSON data transformed: ", merged.transform(json_tree)) diff --git a/examples/advanced/csv.lark b/examples/advanced/csv.lark new file mode 100644 index 0000000..cc2b675 --- /dev/null +++ b/examples/advanced/csv.lark @@ -0,0 +1,14 @@ +start: header _NL row+ +header: "#" " "? (WORD _SEPARATOR?)+ +row: (_anything _SEPARATOR?)+ _NL +_anything: INT | WORD | NON_SEPARATOR_STRING | FLOAT | SIGNED_FLOAT +NON_SEPARATOR_STRING: /[a-zA-z.;\\\/]+/ +_SEPARATOR: /[ ]+/ + | "\t" + | "," + +%import common.NEWLINE -> _NL +%import common.WORD +%import common.INT +%import common.FLOAT +%import common.SIGNED_FLOAT diff --git a/examples/advanced/json.lark b/examples/advanced/json.lark new file mode 100644 index 0000000..bb77c35 --- /dev/null +++ b/examples/advanced/json.lark @@ -0,0 +1,19 @@ +?start: value + +?value: object + | array + | string + | SIGNED_NUMBER -> number + | "true" -> true + | "false" -> false + | "null" -> null + +array : "[" _WS? [value ("," _WS? value)*] "]" +object : "{" _WS? [pair ("," _WS? pair)*] "}" +pair : string ":" _WS value + +string : ESCAPED_STRING + +%import common.ESCAPED_STRING +%import common.SIGNED_NUMBER +%import common.WS -> _WS diff --git a/examples/advanced/storage.lark b/examples/advanced/storage.lark new file mode 100644 index 0000000..64718ed --- /dev/null +++ b/examples/advanced/storage.lark @@ -0,0 +1,8 @@ +start: csv__start + | json__start + +// Renaming of the import variables is required, as they +// receive the namespace of this file. +// See: https://github.com/lark-parser/lark/pull/973#issuecomment-907287565 +%import .csv.start -> csv__start +%import .json.start -> json__start diff --git a/lark/visitors.py b/lark/visitors.py index 4894cab..498a676 100644 --- a/lark/visitors.py +++ b/lark/visitors.py @@ -151,9 +151,16 @@ class Transformer(_Decoratable): def merge_transformers(base_transformer=None, **kwargs): """ - Add the methods of other transformer to this one. + 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. - This method is meant to aid in the maintenance of imports. + Compose a new transformer from a base and the in the `**kwargs` provided Transformer instances. + + The key should match the grammar file that the Transformer is supposed to manipulate. + + This method is meant to aid the composing of large transformers that + manipulate grammars that cross multiple lark files. Example: ```python @@ -183,11 +190,12 @@ def merge_transformers(base_transformer=None, **kwargs): In the above code block `regular_transformer` and `composed_transformer` should behave identically. """ + infix = "__" if base_transformer is None: base_transformer = Transformer() for prefix, transformer in kwargs.items(): - prefix += "__" + prefix += infix for method_name in dir(transformer): method = getattr(transformer, method_name) From afbdb6a4a9087613cf870aa56cb15b2e3fd2b5a7 Mon Sep 17 00:00:00 2001 From: Erez Sh Date: Sat, 28 Aug 2021 10:14:10 +0100 Subject: [PATCH 3/6] Edits to merge_transformers --- lark/visitors.py | 80 ++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/lark/visitors.py b/lark/visitors.py index 498a676..3d65a13 100644 --- a/lark/visitors.py +++ b/lark/visitors.py @@ -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 From d44951383971f5e49a71ca7b712f146786730b38 Mon Sep 17 00:00:00 2001 From: Erez Sh Date: Sat, 28 Aug 2021 10:28:38 +0100 Subject: [PATCH 4/6] Tests: Updated test_merge_transformers --- tests/test_trees.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tests/test_trees.py b/tests/test_trees.py index 730b80b..82bf6c9 100644 --- a/tests/test_trees.py +++ b/tests/test_trees.py @@ -264,10 +264,7 @@ class TestTrees(TestCase): main = sum start = list def module__main(self, children): - prod = 1 - for child in children: - prod *= child - return prod + return sum(children) class T2(Transformer): A = int @@ -277,25 +274,21 @@ class TestTrees(TestCase): class T3(Transformer): def main(self, children): - prod = 1 - for child in children: - prod *= child - return prod + return sum(children) class T4(Transformer): - def other_aspect(self, children): - pass + main = sum + t1_res = T1().transform(tree) composed_res = merge_transformers(T2(), module=T3()).transform(tree) self.assertEqual(t1_res, composed_res) + + composed_res2 = merge_transformers(T2(), module=T4()).transform(tree) + self.assertEqual(t1_res, composed_res2) + with self.assertRaises(AttributeError): merge_transformers(T1(), module=T3()) - try: - composed = merge_transformers(T1(), module=T4()) - except AttributeError: - self.fail("Should be able to add classes that do not conflict") - if __name__ == '__main__': unittest.main() From ca1131a3a19ce2817dc95131333e81d4d20b168c Mon Sep 17 00:00:00 2001 From: Erez Sh Date: Sat, 28 Aug 2021 11:13:01 +0100 Subject: [PATCH 5/6] Examples: Moved example into 'composition' folder, and improved it --- examples/advanced/advanced_transformers.py | 67 ------------------- examples/composition/README.md | 10 +++ .../composition/combined_csv_and_json.txt | 6 ++ examples/{advanced => composition}/csv.lark | 0 examples/composition/eval_csv.py | 24 +++++++ examples/composition/eval_json.py | 15 +++++ examples/{advanced => composition}/json.lark | 0 examples/composition/main.py | 51 ++++++++++++++ .../{advanced => composition}/storage.lark | 4 +- 9 files changed, 108 insertions(+), 69 deletions(-) delete mode 100644 examples/advanced/advanced_transformers.py create mode 100644 examples/composition/README.md create mode 100644 examples/composition/combined_csv_and_json.txt rename examples/{advanced => composition}/csv.lark (100%) create mode 100644 examples/composition/eval_csv.py create mode 100644 examples/composition/eval_json.py rename examples/{advanced => composition}/json.lark (100%) create mode 100644 examples/composition/main.py rename examples/{advanced => composition}/storage.lark (79%) diff --git a/examples/advanced/advanced_transformers.py b/examples/advanced/advanced_transformers.py deleted file mode 100644 index 9810f44..0000000 --- a/examples/advanced/advanced_transformers.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -Transformer merging -================== - -This example is intended to show how transformers can be merged in order to -keep the individual steps clean and simple. - -.. note:: - The imported rules will have to be aliased according to the file it is in. - (See `storage.lark` for an implementation of this idea.) -""" -from lark import Lark, Tree -from json import dumps -from lark.visitors import Transformer, merge_transformers, v_args - -class JsonTreeToJson(Transformer): - @v_args(inline=True) - def string(self, s): - return s[1:-1].replace('\\"', '"') - - array = list - pair = tuple - object = dict - number = v_args(inline=True)(float) - - null = lambda self, _: None - true = lambda self, _: True - false = lambda self, _: False - -class CsvTreeToPandasDict(Transformer): - INT = int - FLOAT = float - SIGNED_FLOAT = float - WORD = str - NON_SEPARATOR_STRING = str - - def row(self, children): - return children - - def start(self, children): - data = {} - - header = children[0].children - for heading in header: - data[heading] = [] - - for row in children[1:]: - for i, element in enumerate(row): - data[header[i]].append(element) - - return data - -class Base(Transformer): - def start(self, children): - return children[0] - -if __name__ == "__main__": - merged = merge_transformers(Base(), csv=CsvTreeToPandasDict(), json=JsonTreeToJson()) - parser = Lark.open("storage.lark") - csv_tree = parser.parse("""# file lines author -data.json 12 Robin -data.csv 30 erezsh -compiler.py 123123 Megalng -""") - print("CSV data in pandas form:", merged.transform(csv_tree)) - json_tree = parser.parse(dumps({"test": "a", "dict": { "list": [1, 1.2] }})) - print("JSON data transformed: ", merged.transform(json_tree)) diff --git a/examples/composition/README.md b/examples/composition/README.md new file mode 100644 index 0000000..259a66a --- /dev/null +++ b/examples/composition/README.md @@ -0,0 +1,10 @@ +Grammar Composition +=================== + +This example shows how to do grammar composition in Lark, by creating a new +file format that allows both CSV and JSON to co-exist. + +We show how, by using namespaces, Lark grammars and their transformers can be fully reused - +they don't need to care if their grammar is used directly, or being imported, or who is doing the importing. + +See [``main.py``](main.py) for more details. \ No newline at end of file diff --git a/examples/composition/combined_csv_and_json.txt b/examples/composition/combined_csv_and_json.txt new file mode 100644 index 0000000..5b8df82 --- /dev/null +++ b/examples/composition/combined_csv_and_json.txt @@ -0,0 +1,6 @@ +{"header": ["this", "is", "json", 1111]} +# file lines author +data.json 12 Robin +data.csv 30 erezsh +compiler.py 123123 Megalng +{"footer": "done"} diff --git a/examples/advanced/csv.lark b/examples/composition/csv.lark similarity index 100% rename from examples/advanced/csv.lark rename to examples/composition/csv.lark diff --git a/examples/composition/eval_csv.py b/examples/composition/eval_csv.py new file mode 100644 index 0000000..3323936 --- /dev/null +++ b/examples/composition/eval_csv.py @@ -0,0 +1,24 @@ +from lark import Transformer + +class CsvTreeToPandasDict(Transformer): + INT = int + FLOAT = float + SIGNED_FLOAT = float + WORD = str + NON_SEPARATOR_STRING = str + + def row(self, children): + return children + + def start(self, children): + data = {} + + header = children[0].children + for heading in header: + data[heading] = [] + + for row in children[1:]: + for i, element in enumerate(row): + data[header[i]].append(element) + + return data diff --git a/examples/composition/eval_json.py b/examples/composition/eval_json.py new file mode 100644 index 0000000..26bf501 --- /dev/null +++ b/examples/composition/eval_json.py @@ -0,0 +1,15 @@ +from lark import Transformer, v_args + +class JsonTreeToJson(Transformer): + @v_args(inline=True) + def string(self, s): + return s[1:-1].replace('\\"', '"') + + array = list + pair = tuple + object = dict + number = v_args(inline=True)(float) + + null = lambda self, _: None + true = lambda self, _: True + false = lambda self, _: False diff --git a/examples/advanced/json.lark b/examples/composition/json.lark similarity index 100% rename from examples/advanced/json.lark rename to examples/composition/json.lark diff --git a/examples/composition/main.py b/examples/composition/main.py new file mode 100644 index 0000000..a549abe --- /dev/null +++ b/examples/composition/main.py @@ -0,0 +1,51 @@ +""" +Grammar Composition +=================== + +This example shows how to do grammar composition in Lark, by creating a new +file format that allows both CSV and JSON to co-exist. + +1) We define ``storage.lark``, which imports both ``csv.lark`` and ``json.lark``, + and allows them to be used one after the other. + + In the generated tree, each imported rule/terminal is automatically prefixed (with ``json__`` or ``csv__), + which creates an implicit namespace and allows them to coexist without collisions. + +2) We merge their respective transformers (unaware of each other) into a new base transformer. + The resulting transformer can evaluate both JSON and CSV in the parse tree. + + The methods of each transformer are renamed into their appropriate namespace, using the given prefix. + This appraoch allows full re-use: the transformers don't need to care if their grammar is used directly, + or being imported, or who is doing the importing. + +""" +from pathlib import Path +from lark import Lark +from json import dumps +from lark.visitors import Transformer, merge_transformers + +from eval_csv import CsvTreeToPandasDict +from eval_json import JsonTreeToJson + +__dir__ = Path(__file__).parent + +class Storage(Transformer): + def start(self, children): + return children + +storage_transformer = merge_transformers(Storage(), csv=CsvTreeToPandasDict(), json=JsonTreeToJson()) + +parser = Lark.open("storage.lark", rel_to=__file__) + +def main(): + json_tree = parser.parse(dumps({"test": "a", "dict": { "list": [1, 1.2] }})) + res = storage_transformer.transform(json_tree) + print("Just JSON: ", res) + + csv_json_tree = parser.parse(open(__dir__ / 'combined_csv_and_json.txt').read()) + res = storage_transformer.transform(csv_json_tree) + print("JSON + CSV: ", dumps(res, indent=2)) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/advanced/storage.lark b/examples/composition/storage.lark similarity index 79% rename from examples/advanced/storage.lark rename to examples/composition/storage.lark index 64718ed..8e2bacc 100644 --- a/examples/advanced/storage.lark +++ b/examples/composition/storage.lark @@ -1,8 +1,8 @@ -start: csv__start - | json__start +start: (csv__start | json__start _NL?)+ // Renaming of the import variables is required, as they // receive the namespace of this file. // See: https://github.com/lark-parser/lark/pull/973#issuecomment-907287565 %import .csv.start -> csv__start +%import .csv._NL -> _NL %import .json.start -> json__start From d433e306592de534cbe121e9c98c3992a7f28b51 Mon Sep 17 00:00:00 2001 From: Erez Sh Date: Sat, 28 Aug 2021 11:25:21 +0100 Subject: [PATCH 6/6] Docs: Add merge_transformer; fix docstrings for sphinx --- docs/visitors.rst | 7 ++++++- examples/composition/eval_csv.py | 2 ++ examples/composition/eval_json.py | 2 ++ lark/visitors.py | 25 +++++++++++++------------ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/docs/visitors.rst b/docs/visitors.rst index f263712..43d0513 100644 --- a/docs/visitors.rst +++ b/docs/visitors.rst @@ -103,12 +103,17 @@ v_args .. autofunction:: lark.visitors.v_args +merge_transformers +------------------ + +.. autofunction:: lark.visitors.merge_transformers + Discard ------- .. autoclass:: lark.visitors.Discard VisitError -------- +---------- .. autoclass:: lark.exceptions.VisitError \ No newline at end of file diff --git a/examples/composition/eval_csv.py b/examples/composition/eval_csv.py index 3323936..8b83f08 100644 --- a/examples/composition/eval_csv.py +++ b/examples/composition/eval_csv.py @@ -1,3 +1,5 @@ +"Transformer for evaluating csv.lark" + from lark import Transformer class CsvTreeToPandasDict(Transformer): diff --git a/examples/composition/eval_json.py b/examples/composition/eval_json.py index 26bf501..c665a19 100644 --- a/examples/composition/eval_json.py +++ b/examples/composition/eval_json.py @@ -1,3 +1,5 @@ +"Transformer for evaluating json.lark" + from lark import Transformer, v_args class JsonTreeToJson(Transformer): diff --git a/lark/visitors.py b/lark/visitors.py index 3d65a13..e2f8b53 100644 --- a/lark/visitors.py +++ b/lark/visitors.py @@ -159,7 +159,7 @@ def merge_transformers(base_transformer=None, **transformers_to_merge): 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. - Paramaters: + Parameters: 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``. @@ -167,21 +167,22 @@ def merge_transformers(base_transformer=None, **transformers_to_merge): AttributeError: In case of a name collision in the merged methods Example: - ```python - class TBase(Transformer): - def start(self, children): - return children[0] + 'bar' + :: + + class TBase(Transformer): + def start(self, children): + return children[0] + 'bar' + + class TImportedGrammar(Transformer): + def foo(self, children): + return "foo" - class TImportedGrammar(Transformer): - def foo(self, children): - return "foo" + composed_transformer = merge_transformers(TBase(), imported=TImportedGrammar()) - composed_transformer = merge_transformers(TBase(), imported=TImportedGrammar()) + t = Tree('start', [ Tree('imported__foo', []) ]) - t = Tree('start', [ Tree('imported__foo', []) ]) + assert composed_transformer.transform(t) == 'foobar' - assert composed_transformer.transform(t) == 'foobar' - ``` """ if base_transformer is None: base_transformer = Transformer()