As discussed in issue #378, when an embedded transformer (that is, one passed to the Lark class using the transformer argument), is an inplace transformer (either a subclass of Transformer_InPlace, or with the @v_args(tree=True) decorator), the in-place transformer was not working correctly and in-fact Lark used it like a normal non-in-place transformer, expecting it to return the transformed value.tags/gm/2021-09-23T00Z/github.com--lark-parser-lark/0.7.2
| @@ -2,6 +2,7 @@ from .exceptions import GrammarError | |||||
| from .lexer import Token | from .lexer import Token | ||||
| from .tree import Tree | from .tree import Tree | ||||
| from .visitors import InlineTransformer # XXX Deprecated | from .visitors import InlineTransformer # XXX Deprecated | ||||
| from .visitors import Transformer_InPlace | |||||
| ###{standalone | ###{standalone | ||||
| from functools import partial, wraps | from functools import partial, wraps | ||||
| @@ -193,6 +194,15 @@ def ptb_inline_args(func): | |||||
| return func(*children) | return func(*children) | ||||
| return f | return f | ||||
| def inplace_transformer(func): | |||||
| @wraps(func) | |||||
| def f(children): | |||||
| # function name in a Transformer is a rule name. | |||||
| tree = Tree(func.__name__, children) | |||||
| func(tree) | |||||
| return tree | |||||
| return f | |||||
| class ParseTreeBuilder: | class ParseTreeBuilder: | ||||
| def __init__(self, rules, tree_class, propagate_positions=False, keep_all_tokens=False, ambiguous=False, maybe_placeholders=False): | def __init__(self, rules, tree_class, propagate_positions=False, keep_all_tokens=False, ambiguous=False, maybe_placeholders=False): | ||||
| self.tree_class = tree_class | self.tree_class = tree_class | ||||
| @@ -231,6 +241,8 @@ class ParseTreeBuilder: | |||||
| # XXX InlineTransformer is deprecated! | # XXX InlineTransformer is deprecated! | ||||
| if getattr(f, 'inline', False) or isinstance(transformer, InlineTransformer): | if getattr(f, 'inline', False) or isinstance(transformer, InlineTransformer): | ||||
| f = ptb_inline_args(f) | f = ptb_inline_args(f) | ||||
| elif hasattr(f, 'whole_tree') or isinstance(transformer, Transformer_InPlace): | |||||
| f = inplace_transformer(f) | |||||
| except AttributeError: | except AttributeError: | ||||
| f = partial(self.tree_class, user_callback_name) | f = partial(self.tree_class, user_callback_name) | ||||
| @@ -20,7 +20,7 @@ logging.basicConfig(level=logging.INFO) | |||||
| from lark.lark import Lark | from lark.lark import Lark | ||||
| from lark.exceptions import GrammarError, ParseError, UnexpectedToken, UnexpectedInput, UnexpectedCharacters | from lark.exceptions import GrammarError, ParseError, UnexpectedToken, UnexpectedInput, UnexpectedCharacters | ||||
| from lark.tree import Tree | from lark.tree import Tree | ||||
| from lark.visitors import Transformer | |||||
| from lark.visitors import Transformer, Transformer_InPlace, v_args | |||||
| from lark.grammar import Rule | from lark.grammar import Rule | ||||
| from lark.lexer import TerminalDef | from lark.lexer import TerminalDef | ||||
| @@ -150,6 +150,31 @@ class TestParsers(unittest.TestCase): | |||||
| r = g.parse("xx") | r = g.parse("xx") | ||||
| self.assertEqual( r.children, ["<c>"] ) | self.assertEqual( r.children, ["<c>"] ) | ||||
| def test_embedded_transformer_inplace(self): | |||||
| class T1(Transformer_InPlace): | |||||
| def a(self, tree): | |||||
| assert isinstance(tree, Tree) | |||||
| tree.children.append("tested") | |||||
| @v_args(tree=True) | |||||
| class T2(Transformer): | |||||
| def a(self, tree): | |||||
| assert isinstance(tree, Tree) | |||||
| tree.children.append("tested") | |||||
| class T3(Transformer): | |||||
| @v_args(tree=True) | |||||
| def a(self, tree): | |||||
| assert isinstance(tree, Tree) | |||||
| tree.children.append("tested") | |||||
| for t in [T1(), T2(), T3()]: | |||||
| g = Lark("""start: a | |||||
| a : "x" | |||||
| """, parser='lalr', transformer=t) | |||||
| r = g.parse("x") | |||||
| first, = r.children | |||||
| self.assertEqual(first.children, ["tested"]) | |||||
| def test_alias(self): | def test_alias(self): | ||||
| Lark("""start: ["a"] "b" ["c"] "e" ["f"] ["g"] ["h"] "x" -> d """) | Lark("""start: ["a"] "b" ["c"] "e" ["f"] ["g"] ["h"] "x" -> d """) | ||||