| @@ -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,14 @@ 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) | |||||
| return func(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 +240,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) | ||||
| @@ -107,7 +107,7 @@ class LALR_ContextualLexer(LALR_WithLexer): | |||||
| ###} | ###} | ||||
| class LALR_CustomLexer(LALR_WithLexer): | class LALR_CustomLexer(LALR_WithLexer): | ||||
| def __init__(self, lexer_cls, lexer_conf, parser_conf, *, options=None): | |||||
| def __init__(self, lexer_cls, lexer_conf, parser_conf, options=None): | |||||
| self.lexer = lexer_cls(self.lexer_conf) | self.lexer = lexer_cls(self.lexer_conf) | ||||
| debug = options.debug if options else False | debug = options.debug if options else False | ||||
| self.parser = LALR_Parser(parser_conf, debug=debug) | self.parser = LALR_Parser(parser_conf, debug=debug) | ||||
| @@ -36,7 +36,7 @@ class Transformer: | |||||
| return f(*children) | return f(*children) | ||||
| elif getattr(f, 'whole_tree', False): | elif getattr(f, 'whole_tree', False): | ||||
| if new_children is not None: | if new_children is not None: | ||||
| raise NotImplementedError("Doesn't work with the base Transformer class") | |||||
| tree.children = new_children | |||||
| return f(tree) | return f(tree) | ||||
| else: | else: | ||||
| return f(children) | return f(children) | ||||
| @@ -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,51 @@ 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): | |||||
| @v_args(tree=True) | |||||
| class T1(Transformer_InPlace): | |||||
| def a(self, tree): | |||||
| assert isinstance(tree, Tree), tree | |||||
| tree.children.append("tested") | |||||
| return tree | |||||
| def b(self, tree): | |||||
| return Tree(tree.data, tree.children + ['tested2']) | |||||
| @v_args(tree=True) | |||||
| class T2(Transformer): | |||||
| def a(self, tree): | |||||
| assert isinstance(tree, Tree) | |||||
| tree.children.append("tested") | |||||
| return tree | |||||
| def b(self, tree): | |||||
| return Tree(tree.data, tree.children + ['tested2']) | |||||
| class T3(Transformer): | |||||
| @v_args(tree=True) | |||||
| def a(self, tree): | |||||
| assert isinstance(tree, Tree) | |||||
| tree.children.append("tested") | |||||
| return tree | |||||
| @v_args(tree=True) | |||||
| def b(self, tree): | |||||
| return Tree(tree.data, tree.children + ['tested2']) | |||||
| for t in [T1(), T2(), T3()]: | |||||
| for internal in [False, True]: | |||||
| g = Lark("""start: a b | |||||
| a : "x" | |||||
| b : "y" | |||||
| """, parser='lalr', transformer=t if internal else None) | |||||
| r = g.parse("xy") | |||||
| if not internal: | |||||
| r = t.transform(r) | |||||
| a, b = r.children | |||||
| self.assertEqual(a.children, ["tested"]) | |||||
| self.assertEqual(b.children, ["tested2"]) | |||||
| 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 """) | ||||