@@ -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 | |||
@@ -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) |
@@ -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'): | |||
@@ -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) |
@@ -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__': | |||