Nearley tool still needs fixingtags/gm/2021-09-23T00Z/github.com--lark-parser-lark/0.6.0
@@ -2,7 +2,7 @@ | |||||
# This example shows how to write a basic calculator with variables. | # This example shows how to write a basic calculator with variables. | ||||
# | # | ||||
from lark import Lark, Transformer, children_args_inline | |||||
from lark import Lark, Transformer, visitor_args | |||||
try: | try: | ||||
input = raw_input # For Python2 compatibility | input = raw_input # For Python2 compatibility | ||||
@@ -34,7 +34,8 @@ calc_grammar = """ | |||||
%ignore WS_INLINE | %ignore WS_INLINE | ||||
""" | """ | ||||
class CalculateTree(SimpleTransformer): | |||||
@visitor_args(inline=True) | |||||
class CalculateTree(Transformer): | |||||
from operator import add, sub, mul, truediv as div, neg | from operator import add, sub, mul, truediv as div, neg | ||||
number = float | number = float | ||||
@@ -7,7 +7,7 @@ | |||||
import sys | import sys | ||||
from lark import Lark, inline_args, Transformer | |||||
from lark import Lark, Transformer, visitor_args | |||||
json_grammar = r""" | json_grammar = r""" | ||||
?start: value | ?start: value | ||||
@@ -34,14 +34,14 @@ json_grammar = r""" | |||||
""" | """ | ||||
class TreeToJson(Transformer): | class TreeToJson(Transformer): | ||||
@inline_args | |||||
@visitor_args(inline=True) | |||||
def string(self, s): | def string(self, s): | ||||
return s[1:-1].replace('\\"', '"') | return s[1:-1].replace('\\"', '"') | ||||
array = list | array = list | ||||
pair = tuple | pair = tuple | ||||
object = dict | object = dict | ||||
number = inline_args(float) | |||||
number = visitor_args(inline=True)(float) | |||||
null = lambda self, _: None | null = lambda self, _: None | ||||
true = lambda self, _: True | true = lambda self, _: True | ||||
@@ -1,5 +1,6 @@ | |||||
from .tree import Tree | from .tree import Tree | ||||
from .visitors import Transformer, Visitor, children_args, children_args_inline | |||||
from .visitors import Transformer, Visitor, visitor_args, Discard | |||||
from .visitors import InlineTransformer, inline_args # XXX Deprecated | |||||
from .common import ParseError, GrammarError, UnexpectedToken | from .common import ParseError, GrammarError, UnexpectedToken | ||||
from .lexer import UnexpectedInput, LexError | from .lexer import UnexpectedInput, LexError | ||||
from .lark import Lark | from .lark import Lark | ||||
@@ -16,7 +16,7 @@ from .grammar import RuleOptions, Rule, Terminal, NonTerminal, Symbol | |||||
from .utils import classify, suppress | from .utils import classify, suppress | ||||
from .tree import Tree, SlottedTree as ST | from .tree import Tree, SlottedTree as ST | ||||
from .visitors import Transformer, Visitor, children_args, children_args_inline | |||||
from .visitors import Transformer, Visitor, visitor_args | |||||
__path__ = os.path.dirname(__file__) | __path__ = os.path.dirname(__file__) | ||||
IMPORT_PATHS = [os.path.join(__path__, 'grammars')] | IMPORT_PATHS = [os.path.join(__path__, 'grammars')] | ||||
@@ -138,7 +138,7 @@ RULES = { | |||||
} | } | ||||
@children_args_inline | |||||
@visitor_args(inline=True) | |||||
class EBNF_to_BNF(Transformer): | class EBNF_to_BNF(Transformer): | ||||
def __init__(self): | def __init__(self): | ||||
self.new_rules = [] | self.new_rules = [] | ||||
@@ -232,7 +232,6 @@ class SimplifyRule_Visitor(Visitor): | |||||
tree.children = list(set(tree.children)) | tree.children = list(set(tree.children)) | ||||
@children_args | |||||
class RuleTreeToText(Transformer): | class RuleTreeToText(Transformer): | ||||
def expansions(self, x): | def expansions(self, x): | ||||
return x | return x | ||||
@@ -244,7 +243,7 @@ class RuleTreeToText(Transformer): | |||||
return expansion, alias.value | return expansion, alias.value | ||||
@children_args_inline | |||||
@visitor_args(inline=True) | |||||
class CanonizeTree(Transformer): | class CanonizeTree(Transformer): | ||||
def maybe(self, expr): | def maybe(self, expr): | ||||
return ST('expr', [expr, Token('OP', '?', -1)]) | return ST('expr', [expr, Token('OP', '?', -1)]) | ||||
@@ -265,7 +264,7 @@ class PrepareAnonTerminals(Transformer): | |||||
self.i = 0 | self.i = 0 | ||||
@children_args_inline | |||||
@visitor_args(inline=True) | |||||
def pattern(self, p): | def pattern(self, p): | ||||
value = p.value | value = p.value | ||||
if p in self.token_reverse and p.flags != self.token_reverse[p].pattern.flags: | if p in self.token_reverse and p.flags != self.token_reverse[p].pattern.flags: | ||||
@@ -355,7 +354,7 @@ def _literal_to_pattern(literal): | |||||
'REGEXP': PatternRE }[literal.type](s, flags) | 'REGEXP': PatternRE }[literal.type](s, flags) | ||||
@children_args_inline | |||||
@visitor_args(inline=True) | |||||
class PrepareLiterals(Transformer): | class PrepareLiterals(Transformer): | ||||
def literal(self, literal): | def literal(self, literal): | ||||
return ST('pattern', [_literal_to_pattern(literal)]) | return ST('pattern', [_literal_to_pattern(literal)]) | ||||
@@ -369,7 +368,6 @@ class PrepareLiterals(Transformer): | |||||
return ST('pattern', [PatternRE(regexp)]) | return ST('pattern', [PatternRE(regexp)]) | ||||
@children_args | |||||
class TokenTreeToPattern(Transformer): | class TokenTreeToPattern(Transformer): | ||||
def pattern(self, ps): | def pattern(self, ps): | ||||
p ,= ps | p ,= ps | ||||
@@ -410,7 +408,6 @@ class TokenTreeToPattern(Transformer): | |||||
return v[0] | return v[0] | ||||
class PrepareSymbols(Transformer): | class PrepareSymbols(Transformer): | ||||
@children_args | |||||
def value(self, v): | def value(self, v): | ||||
v ,= v | v ,= v | ||||
if isinstance(v, Tree): | if isinstance(v, Tree): | ||||
@@ -535,7 +532,7 @@ def options_from_rule(name, *x): | |||||
def symbols_from_strcase(expansion): | def symbols_from_strcase(expansion): | ||||
return [Terminal(x, filter_out=x.startswith('_')) if is_terminal(x) else NonTerminal(x) for x in expansion] | return [Terminal(x, filter_out=x.startswith('_')) if is_terminal(x) else NonTerminal(x) for x in expansion] | ||||
@children_args_inline | |||||
@visitor_args(inline=True) | |||||
class PrepareGrammar(Transformer): | class PrepareGrammar(Transformer): | ||||
def terminal(self, name): | def terminal(self, name): | ||||
return name | return name | ||||
@@ -3,9 +3,10 @@ from .utils import suppress | |||||
from .lexer import Token | from .lexer import Token | ||||
from .grammar import Rule | from .grammar import Rule | ||||
from .tree import Tree | from .tree import Tree | ||||
from .visitors import InlineTransformer # XXX Deprecated | |||||
###{standalone | ###{standalone | ||||
from functools import partial | |||||
from functools import partial, wraps | |||||
class ExpandSingleChild: | class ExpandSingleChild: | ||||
@@ -95,6 +96,15 @@ def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous): | |||||
class Callback(object): | class Callback(object): | ||||
pass | pass | ||||
def inline_args(func): | |||||
@wraps(func) | |||||
def f(children): | |||||
return func(*children) | |||||
return f | |||||
class ParseTreeBuilder: | class ParseTreeBuilder: | ||||
def __init__(self, rules, tree_class, propagate_positions=False, keep_all_tokens=False, ambiguous=False): | def __init__(self, rules, tree_class, propagate_positions=False, keep_all_tokens=False, ambiguous=False): | ||||
self.tree_class = tree_class | self.tree_class = tree_class | ||||
@@ -130,6 +140,10 @@ class ParseTreeBuilder: | |||||
user_callback_name = rule.alias or rule.origin.name | user_callback_name = rule.alias or rule.origin.name | ||||
try: | try: | ||||
f = getattr(transformer, user_callback_name) | f = getattr(transformer, user_callback_name) | ||||
assert not getattr(f, 'meta', False), "Meta args not supported for internal transformer" | |||||
# XXX InlineTransformer is deprecated! | |||||
if getattr(f, 'inline', False) or isinstance(transformer, InlineTransformer): | |||||
f = inline_args(f) | |||||
except AttributeError: | except AttributeError: | ||||
f = partial(self.tree_class, user_callback_name) | f = partial(self.tree_class, user_callback_name) | ||||
@@ -14,7 +14,7 @@ | |||||
# Email : erezshin@gmail.com | # Email : erezshin@gmail.com | ||||
from ..tree import Tree | from ..tree import Tree | ||||
from ..visitors import Transformer_InPlace | |||||
from ..visitors import Transformer_InPlace, visitor_args | |||||
from ..common import ParseError, UnexpectedToken | from ..common import ParseError, UnexpectedToken | ||||
from .grammar_analysis import GrammarAnalyzer | from .grammar_analysis import GrammarAnalyzer | ||||
from ..grammar import NonTerminal | from ..grammar import NonTerminal | ||||
@@ -114,9 +114,9 @@ class Column: | |||||
if old_tree.data != '_ambig': | if old_tree.data != '_ambig': | ||||
new_tree = old_tree.copy() | new_tree = old_tree.copy() | ||||
new_tree.rule = old_tree.rule | |||||
new_tree.meta.rule = old_tree.meta.rule | |||||
old_tree.set('_ambig', [new_tree]) | old_tree.set('_ambig', [new_tree]) | ||||
old_tree.rule = None # No longer a 'drv' node | |||||
old_tree.meta.rule = None # No longer a 'drv' node | |||||
if item.tree.children[0] is old_tree: # XXX a little hacky! | if item.tree.children[0] is old_tree: # XXX a little hacky! | ||||
raise ParseError("Infinite recursion in grammar! (Rule %s)" % item.rule) | raise ParseError("Infinite recursion in grammar! (Rule %s)" % item.rule) | ||||
@@ -234,5 +234,6 @@ class ApplyCallbacks(Transformer_InPlace): | |||||
def __init__(self, postprocess): | def __init__(self, postprocess): | ||||
self.postprocess = postprocess | self.postprocess = postprocess | ||||
def drv(self, tree): | |||||
return self.postprocess[tree.meta.rule](tree.children) | |||||
@visitor_args(meta=True) | |||||
def drv(self, children, meta): | |||||
return self.postprocess[meta.rule](children) |
@@ -26,8 +26,15 @@ def _compare_priority(tree1, tree2): | |||||
tree1.iter_subtrees() | tree1.iter_subtrees() | ||||
def _compare_drv(tree1, tree2): | def _compare_drv(tree1, tree2): | ||||
rule1 = getattr(tree1.meta, 'rule', None) | |||||
rule2 = getattr(tree2.meta, 'rule', None) | |||||
try: | |||||
rule1 = tree1.meta.rule | |||||
except AttributeError: | |||||
rule1 = None | |||||
try: | |||||
rule2 = tree2.meta.rule | |||||
except AttributeError: | |||||
rule2 = None | |||||
if None == rule1 == rule2: | if None == rule1 == rule2: | ||||
return compare(tree1, tree2) | return compare(tree1, tree2) | ||||
@@ -10,10 +10,10 @@ class Meta: | |||||
###{standalone | ###{standalone | ||||
class Tree(object): | class Tree(object): | ||||
def __init__(self, data, children): | |||||
def __init__(self, data, children, meta=None): | |||||
self.data = data | self.data = data | ||||
self.children = children | self.children = children | ||||
self._meta = None | |||||
self._meta = meta | |||||
@property | @property | ||||
def meta(self): | def meta(self): | ||||
@@ -8,39 +8,23 @@ class Discard(Exception): | |||||
pass | pass | ||||
class Base: | |||||
def _call_userfunc(self, tree): | |||||
return getattr(self, tree.data, self.__default__)(tree) | |||||
# Transformers | |||||
def __default__(self, tree): | |||||
"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): | |||||
class Transformer: | |||||
def _call_userfunc(self, data, children, meta): | |||||
# Assumes tree is already transformed | # Assumes tree is already transformed | ||||
try: | try: | ||||
f = getattr(self, tree.data) | |||||
f = getattr(self, data) | |||||
except AttributeError: | except AttributeError: | ||||
return self.__default__(tree) | |||||
return self.__default__(data, children, meta) | |||||
else: | else: | ||||
return f(tree.children) | |||||
if getattr(f, 'meta', False): | |||||
return f(children, meta) | |||||
elif getattr(f, 'inline', False): | |||||
return f(*children) | |||||
else: | |||||
return f(children) | |||||
class Transformer(Base): | |||||
def _transform_children(self, children): | def _transform_children(self, children): | ||||
for c in children: | for c in children: | ||||
try: | try: | ||||
@@ -49,8 +33,8 @@ class Transformer(Base): | |||||
pass | pass | ||||
def _transform_tree(self, tree): | def _transform_tree(self, tree): | ||||
tree = Tree(tree.data, list(self._transform_children(tree.children))) | |||||
return self._call_userfunc(tree) | |||||
children = list(self._transform_children(tree.children)) | |||||
return self._call_userfunc(tree.data, children, tree.meta) | |||||
def transform(self, tree): | def transform(self, tree): | ||||
return self._transform_tree(tree) | return self._transform_tree(tree) | ||||
@@ -58,6 +42,32 @@ class Transformer(Base): | |||||
def __mul__(self, other): | def __mul__(self, other): | ||||
return TransformerChain(self, other) | return TransformerChain(self, other) | ||||
def __default__(self, data, children, meta): | |||||
"Default operation on tree (for override)" | |||||
return Tree(data, children, meta) | |||||
@classmethod | |||||
def _apply_decorator(cls, decorator, **kwargs): | |||||
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, **kwargs)) | |||||
return cls | |||||
class InlineTransformer(Transformer): # XXX Deprecated | |||||
def _call_userfunc(self, data, children, meta): | |||||
# Assumes tree is already transformed | |||||
try: | |||||
f = getattr(self, data) | |||||
except AttributeError: | |||||
return self.__default__(data, children, meta) | |||||
else: | |||||
return f(*children) | |||||
class TransformerChain(object): | class TransformerChain(object): | ||||
@@ -75,7 +85,7 @@ class TransformerChain(object): | |||||
class Transformer_InPlace(Transformer): | class Transformer_InPlace(Transformer): | ||||
def _transform_tree(self, tree): # Cancel recursion | def _transform_tree(self, tree): # Cancel recursion | ||||
return self._call_userfunc(tree) | |||||
return self._call_userfunc(tree.data, tree.children, tree.meta) | |||||
def transform(self, tree): | def transform(self, tree): | ||||
for subtree in tree.iter_subtrees(): | for subtree in tree.iter_subtrees(): | ||||
@@ -87,11 +97,22 @@ class Transformer_InPlace(Transformer): | |||||
class Transformer_InPlaceRecursive(Transformer): | class Transformer_InPlaceRecursive(Transformer): | ||||
def _transform_tree(self, tree): | def _transform_tree(self, tree): | ||||
tree.children = list(self._transform_children(tree.children)) | tree.children = list(self._transform_children(tree.children)) | ||||
return self._call_userfunc(tree) | |||||
return self._call_userfunc(tree.data, tree.children, tree.meta) | |||||
# Visitors | |||||
class Visitor(Base): | |||||
class VisitorBase: | |||||
def _call_userfunc(self, tree): | |||||
return getattr(self, tree.data, self.__default__)(tree) | |||||
def __default__(self, tree): | |||||
"Default operation on tree (for override)" | |||||
return tree | |||||
class Visitor(VisitorBase): | |||||
"Bottom-up visitor" | "Bottom-up visitor" | ||||
def visit(self, tree): | def visit(self, tree): | ||||
@@ -99,7 +120,7 @@ class Visitor(Base): | |||||
self._call_userfunc(subtree) | self._call_userfunc(subtree) | ||||
return tree | return tree | ||||
class Visitor_Recursive(Base): | |||||
class Visitor_Recursive(VisitorBase): | |||||
def visit(self, tree): | def visit(self, tree): | ||||
for child in tree.children: | for child in tree.children: | ||||
if isinstance(child, Tree): | if isinstance(child, Tree): | ||||
@@ -110,6 +131,7 @@ class Visitor_Recursive(Base): | |||||
return tree | return tree | ||||
def visit_children_decor(func): | def visit_children_decor(func): | ||||
@wraps(func) | @wraps(func) | ||||
def inner(cls, tree): | def inner(cls, tree): | ||||
@@ -117,7 +139,8 @@ def visit_children_decor(func): | |||||
return func(cls, values) | return func(cls, values) | ||||
return inner | return inner | ||||
class Interpreter(object): | |||||
class Interpreter: | |||||
"Top-down visitor" | "Top-down visitor" | ||||
def visit(self, tree): | def visit(self, tree): | ||||
@@ -136,56 +159,58 @@ class Interpreter(object): | |||||
# Decorators | |||||
def _apply_decorator(obj, decorator): | |||||
def _apply_decorator(obj, decorator, **kwargs): | |||||
try: | try: | ||||
_apply = obj._apply_decorator | _apply = obj._apply_decorator | ||||
except AttributeError: | except AttributeError: | ||||
return decorator(obj) | |||||
return decorator(obj, **kwargs) | |||||
else: | else: | ||||
return _apply(decorator) | |||||
return _apply(decorator, **kwargs) | |||||
def _children_args__func(func): | |||||
if getattr(func, '_children_args_decorated', False): | |||||
return func | |||||
def _inline_args__func(func): | |||||
@wraps(func) | @wraps(func) | ||||
def create_decorator(_f, with_self): | def create_decorator(_f, with_self): | ||||
if with_self: | if with_self: | ||||
def f(self, tree): | |||||
return _f(self, tree.children) | |||||
def f(self, children): | |||||
return _f(self, *children) | |||||
else: | else: | ||||
def f(args): | |||||
return _f(tree.children) | |||||
f._children_args_decorated = True | |||||
def f(self, children): | |||||
return _f(*children) | |||||
return f | return f | ||||
return smart_decorator(func, create_decorator) | return smart_decorator(func, create_decorator) | ||||
def children_args(obj): | |||||
return _apply_decorator(obj, _children_args__func) | |||||
def inline_args(obj): # XXX Deprecated | |||||
return _apply_decorator(obj, _inline_args__func) | |||||
def _children_args_inline__func(func): | |||||
if getattr(func, '_children_args_decorated', False): | |||||
return func | |||||
@wraps(func) | |||||
def _visitor_args_func_dec(func, inline=False, meta=False): | |||||
assert not (inline and meta) | |||||
def create_decorator(_f, with_self): | def create_decorator(_f, with_self): | ||||
if with_self: | if with_self: | ||||
def f(self, tree): | |||||
return _f(self, *tree.children) | |||||
def f(self, *args, **kwargs): | |||||
return _f(self, *args, **kwargs) | |||||
else: | else: | ||||
def f(self, tree): | |||||
print ('##', _f, tree) | |||||
return _f(*tree.children) | |||||
f._children_args_decorated = True | |||||
def f(self, *args, **kwargs): | |||||
return _f(*args, **kwargs) | |||||
return f | return f | ||||
return smart_decorator(func, create_decorator) | |||||
f = smart_decorator(func, create_decorator) | |||||
f.inline = inline | |||||
f.meta = meta | |||||
return f | |||||
def visitor_args(inline=False, meta=False): | |||||
if inline and meta: | |||||
raise ValueError("Visitor functions can either accept meta, or be inlined. Not both.") | |||||
def _visitor_args_dec(obj): | |||||
return _apply_decorator(obj, _visitor_args_func_dec, inline=inline, meta=meta) | |||||
return _visitor_args_dec | |||||
def children_args_inline(obj): | |||||
return _apply_decorator(obj, _children_args_inline__func) |
@@ -21,7 +21,7 @@ from lark.lark import Lark | |||||
from lark.common import GrammarError, ParseError, UnexpectedToken | from lark.common import GrammarError, ParseError, UnexpectedToken | ||||
from lark.lexer import LexError, UnexpectedInput | from lark.lexer import LexError, UnexpectedInput | ||||
from lark.tree import Tree | from lark.tree import Tree | ||||
from lark.visitors import Transformer, children_args | |||||
from lark.visitors import Transformer | |||||
__path__ = os.path.dirname(__file__) | __path__ = os.path.dirname(__file__) | ||||
def _read(n, *args): | def _read(n, *args): | ||||
@@ -93,7 +93,6 @@ class TestParsers(unittest.TestCase): | |||||
self.assertEqual( r.children[0].data, "c" ) | self.assertEqual( r.children[0].data, "c" ) | ||||
def test_embedded_transformer(self): | def test_embedded_transformer(self): | ||||
@children_args | |||||
class T(Transformer): | class T(Transformer): | ||||
def a(self, children): | def a(self, children): | ||||
return "<a>" | return "<a>" | ||||
@@ -6,7 +6,7 @@ import copy | |||||
import pickle | import pickle | ||||
from lark.tree import Tree | from lark.tree import Tree | ||||
from lark.visitors import Transformer, Interpreter, visit_children_decor, children_args_inline, children_args | |||||
from lark.visitors import Transformer, Interpreter, visit_children_decor, visitor_args | |||||
class TestTrees(TestCase): | class TestTrees(TestCase): | ||||
@@ -63,19 +63,18 @@ class TestTrees(TestCase): | |||||
t = Tree('add', [Tree('sub', [Tree('i', ['3']), Tree('f', ['1.1'])]), Tree('i', ['1'])]) | t = Tree('add', [Tree('sub', [Tree('i', ['3']), Tree('f', ['1.1'])]), Tree('i', ['1'])]) | ||||
class T(Transformer): | class T(Transformer): | ||||
i = children_args_inline(int) | |||||
f = children_args_inline(float) | |||||
i = visitor_args(inline=True)(int) | |||||
f = visitor_args(inline=True)(float) | |||||
sub = lambda self, tree: tree.children[0] - tree.children[1] | |||||
def add(self, tree): | |||||
return sum(tree.children) | |||||
sub = lambda self, values: values[0] - values[1] | |||||
def add(self, values): | |||||
return sum(values) | |||||
res = T().transform(t) | res = T().transform(t) | ||||
self.assertEqual(res, 2.9) | self.assertEqual(res, 2.9) | ||||
@children_args_inline | |||||
@visitor_args(inline=True) | |||||
class T(Transformer): | class T(Transformer): | ||||
i = int | i = int | ||||
f = float | f = float | ||||
@@ -89,7 +88,7 @@ class TestTrees(TestCase): | |||||
self.assertEqual(res, 2.9) | self.assertEqual(res, 2.9) | ||||
@children_args_inline | |||||
@visitor_args(inline=True) | |||||
class T(Transformer): | class T(Transformer): | ||||
i = int | i = int | ||||
f = float | f = float | ||||
@@ -99,19 +98,6 @@ class TestTrees(TestCase): | |||||
self.assertEqual(res, 2.9) | 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__': | if __name__ == '__main__': | ||||
unittest.main() | unittest.main() | ||||