@@ -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 """) | ||||