diff --git a/examples/calc.py b/examples/calc.py index cb7ef5d..e90b5cc 100644 --- a/examples/calc.py +++ b/examples/calc.py @@ -2,7 +2,7 @@ # This example shows how to write a basic calculator with variables. # -from lark import Lark, InlineTransformer +from lark import Lark, Transformer, children_args_inline try: input = raw_input # For Python2 compatibility @@ -34,7 +34,7 @@ calc_grammar = """ %ignore WS_INLINE """ -class CalculateTree(InlineTransformer): +class CalculateTree(SimpleTransformer): from operator import add, sub, mul, truediv as div, neg number = float diff --git a/lark/parsers/earley.py b/lark/parsers/earley.py index d58b57c..87dc41e 100644 --- a/lark/parsers/earley.py +++ b/lark/parsers/earley.py @@ -21,11 +21,10 @@ from ..grammar import NonTerminal class Derivation(Tree): - _hash = None - def __init__(self, rule, items=None): Tree.__init__(self, 'drv', items or []) - self.rule = rule + self.meta.rule = rule + self._hash = None def _pretty_label(self): # Nicer pretty for debugging the parser return self.rule.origin if self.rule else self.data @@ -236,4 +235,4 @@ class ApplyCallbacks(Transformer_InPlace): self.postprocess = postprocess def drv(self, tree): - return self.postprocess[tree.rule](tree.children) + return self.postprocess[tree.meta.rule](tree.children) diff --git a/lark/parsers/resolve_ambig.py b/lark/parsers/resolve_ambig.py index 7c482ae..0d3f17c 100644 --- a/lark/parsers/resolve_ambig.py +++ b/lark/parsers/resolve_ambig.py @@ -16,7 +16,7 @@ def _sum_priority(tree): for n in tree.iter_subtrees(): try: - p += n.rule.options.priority or 0 + p += n.meta.rule.options.priority or 0 except AttributeError: pass @@ -26,8 +26,8 @@ def _compare_priority(tree1, tree2): tree1.iter_subtrees() def _compare_drv(tree1, tree2): - rule1 = getattr(tree1, 'rule', None) - rule2 = getattr(tree2, 'rule', None) + rule1 = getattr(tree1.meta, 'rule', None) + rule2 = getattr(tree2.meta, 'rule', None) if None == rule1 == rule2: return compare(tree1, tree2) @@ -45,7 +45,7 @@ def _compare_drv(tree1, tree2): if c: return c - c = _compare_rules(tree1.rule, tree2.rule) + c = _compare_rules(tree1.meta.rule, tree2.meta.rule) if c: return c @@ -65,7 +65,7 @@ def _standard_resolve_ambig(tree): best = max(tree.children, key=key_f) assert best.data == 'drv' tree.set('drv', best.children) - tree.rule = best.rule # needed for applying callbacks + tree.meta.rule = best.meta.rule # needed for applying callbacks def standard_resolve_ambig(tree): for ambig in tree.find_data('_ambig'): @@ -93,7 +93,7 @@ def _antiscore_sum_resolve_ambig(tree): best = min(tree.children, key=_antiscore_sum_drv) assert best.data == 'drv' tree.set('drv', best.children) - tree.rule = best.rule # needed for applying callbacks + tree.meta.rule = best.meta.rule # needed for applying callbacks def antiscore_sum_resolve_ambig(tree): for ambig in tree.find_data('_ambig'): diff --git a/lark/visitors.py b/lark/visitors.py index a1167fa..d3853bf 100644 --- a/lark/visitors.py +++ b/lark/visitors.py @@ -1,4 +1,4 @@ -from inspect import isclass +from inspect import isclass, getmembers, getmro from functools import wraps from .utils import smart_decorator @@ -16,6 +16,30 @@ class Base: "Default operation on tree (for override)" return tree + @classmethod + def _apply_decorator(cls, decorator): + mro = getmro(cls) + assert mro[0] is cls + libmembers = {name for _cls in mro[1:] for name, _ in getmembers(_cls)} + for name, value in getmembers(cls): + if name.startswith('_') or name in libmembers: + continue + + setattr(cls, name, decorator(value)) + return cls + + +class SimpleBase(Base): + def _call_userfunc(self, tree): + # Assumes tree is already transformed + try: + f = getattr(self, tree.data) + except AttributeError: + return self.__default__(tree) + else: + return f(tree.children) + + class Transformer(Base): def _transform_children(self, children): for c in children: @@ -35,6 +59,7 @@ class Transformer(Base): return TransformerChain(self, other) + class TransformerChain(object): def __init__(self, *transformers): self.transformers = transformers @@ -110,8 +135,22 @@ class Interpreter(object): -def _children_args__func(f): - @wraps(f) + + +def _apply_decorator(obj, decorator): + try: + _apply = obj._apply_decorator + except AttributeError: + return decorator(obj) + else: + return _apply(decorator) + + +def _children_args__func(func): + if getattr(func, '_children_args_decorated', False): + return func + + @wraps(func) def create_decorator(_f, with_self): if with_self: def f(self, tree): @@ -119,55 +158,34 @@ def _children_args__func(f): else: def f(args): return _f(tree.children) + f._children_args_decorated = True return f - return smart_decorator(f, create_decorator) - -def _children_args__class(cls): - def _call_userfunc(self, tree): - # Assumes tree is already transformed - try: - f = getattr(self, tree.data) - except AttributeError: - return self.__default__(tree) - else: - return f(tree.children) - cls._call_userfunc = _call_userfunc - return cls - + return smart_decorator(func, create_decorator) def children_args(obj): - decorator = _children_args__class if isclass(obj) and issubclass(obj, Base) else _children_args__func - return decorator(obj) + return _apply_decorator(obj, _children_args__func) -def _children_args_inline__func(f): - @wraps(f) +def _children_args_inline__func(func): + if getattr(func, '_children_args_decorated', False): + return func + + @wraps(func) def create_decorator(_f, with_self): if with_self: def f(self, tree): return _f(self, *tree.children) else: - def f(args): + def f(self, tree): + print ('##', _f, tree) return _f(*tree.children) + f._children_args_decorated = True return f - return smart_decorator(f, create_decorator) - + return smart_decorator(func, create_decorator) -def _children_args_inline__class(cls): - def _call_userfunc(self, tree): - # Assumes tree is already transformed - try: - f = getattr(self, tree.data) - except AttributeError: - return self.__default__(tree) - else: - return f(*tree.children) - cls._call_userfunc = _call_userfunc - return cls def children_args_inline(obj): - decorator = _children_args_inline__class if isclass(obj) and issubclass(obj, Base) else _children_args_inline__func - return decorator(obj) + return _apply_decorator(obj, _children_args_inline__func) diff --git a/tests/test_trees.py b/tests/test_trees.py index df3b9b6..b7796bf 100644 --- a/tests/test_trees.py +++ b/tests/test_trees.py @@ -6,7 +6,7 @@ import copy import pickle from lark.tree import Tree -from lark.visitors import Interpreter, visit_children_decor +from lark.visitors import Transformer, Interpreter, visit_children_decor, children_args_inline, children_args class TestTrees(TestCase): @@ -59,6 +59,58 @@ class TestTrees(TestCase): self.assertEqual(Interp3().visit(t), list('BCd')) + def test_transformer(self): + t = Tree('add', [Tree('sub', [Tree('i', ['3']), Tree('f', ['1.1'])]), Tree('i', ['1'])]) + + class T(Transformer): + i = children_args_inline(int) + f = children_args_inline(float) + + sub = lambda self, tree: tree.children[0] - tree.children[1] + + def add(self, tree): + return sum(tree.children) + + + res = T().transform(t) + self.assertEqual(res, 2.9) + + @children_args_inline + class T(Transformer): + i = int + f = float + sub = lambda self, a, b: a-b + + def add(self, a, b): + return a + b + + + res = T().transform(t) + self.assertEqual(res, 2.9) + + + @children_args_inline + class T(Transformer): + i = int + f = float + from operator import sub, add + + res = T().transform(t) + self.assertEqual(res, 2.9) + + + @children_args + class T(Transformer): + i = children_args_inline(int) + f = children_args_inline(float) + + sub = lambda self, values: values[0] - values[1] + + def add(self, values): + return sum(values) + + res = T().transform(t) + self.assertEqual(res, 2.9) if __name__ == '__main__':