Browse Source

Refactored transformers, better code

tags/gm/2021-09-23T00Z/github.com--lark-parser-lark/0.6.0
Erez Shinan 7 years ago
parent
commit
9daacb9082
6 changed files with 98 additions and 73 deletions
  1. +0
    -1
      lark/__init__.py
  2. +17
    -14
      lark/load_grammar.py
  3. +74
    -39
      lark/transformers.py
  4. +0
    -2
      lark/tree.py
  5. +5
    -15
      lark/utils.py
  6. +2
    -2
      tests/test_parser.py

+ 0
- 1
lark/__init__.py View File

@@ -3,6 +3,5 @@ from .transformers import Transformer
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
from .utils import inline_args


__version__ = "0.5.6" __version__ = "0.5.6"

+ 17
- 14
lark/load_grammar.py View File

@@ -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 .transformers import Transformer, ChildrenTransformer, inline_args, Visitor
from .transformers import Transformer, Visitor, children_args, children_args_inline


__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,8 +138,8 @@ RULES = {
} }




@inline_args
class EBNF_to_BNF(ChildrenTransformer):
@children_args_inline
class EBNF_to_BNF(Transformer):
def __init__(self): def __init__(self):
self.new_rules = [] self.new_rules = []
self.rules_by_expr = {} self.rules_by_expr = {}
@@ -232,7 +232,8 @@ class SimplifyRule_Visitor(Visitor):
tree.children = list(set(tree.children)) tree.children = list(set(tree.children))




class RuleTreeToText(ChildrenTransformer):
@children_args
class RuleTreeToText(Transformer):
def expansions(self, x): def expansions(self, x):
return x return x
def expansion(self, symbols): def expansion(self, symbols):
@@ -243,8 +244,8 @@ class RuleTreeToText(ChildrenTransformer):
return expansion, alias.value return expansion, alias.value




@inline_args
class CanonizeTree(ChildrenTransformer):
@children_args_inline
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)])


@@ -254,8 +255,8 @@ class CanonizeTree(ChildrenTransformer):
tokenmods, value = args tokenmods, value = args
return tokenmods + [value] return tokenmods + [value]


@inline_args
class PrepareAnonTerminals(ChildrenTransformer):
@children_args_inline
class PrepareAnonTerminals(Transformer):
"Create a unique list of anonymous tokens. Attempt to give meaningful names to them when we add them" "Create a unique list of anonymous tokens. Attempt to give meaningful names to them when we add them"


def __init__(self, tokens): def __init__(self, tokens):
@@ -354,8 +355,8 @@ def _literal_to_pattern(literal):
'REGEXP': PatternRE }[literal.type](s, flags) 'REGEXP': PatternRE }[literal.type](s, flags)




@inline_args
class PrepareLiterals(ChildrenTransformer):
@children_args_inline
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)])


@@ -368,7 +369,8 @@ class PrepareLiterals(ChildrenTransformer):
return ST('pattern', [PatternRE(regexp)]) return ST('pattern', [PatternRE(regexp)])




class TokenTreeToPattern(ChildrenTransformer):
@children_args
class TokenTreeToPattern(Transformer):
def pattern(self, ps): def pattern(self, ps):
p ,= ps p ,= ps
return p return p
@@ -407,7 +409,8 @@ class TokenTreeToPattern(ChildrenTransformer):
def value(self, v): def value(self, v):
return v[0] return v[0]


class PrepareSymbols(ChildrenTransformer):
@children_args
class PrepareSymbols(Transformer):
def value(self, v): def value(self, v):
v ,= v v ,= v
if isinstance(v, Tree): if isinstance(v, Tree):
@@ -532,8 +535,8 @@ 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]


@inline_args
class PrepareGrammar(ChildrenTransformer):
@children_args_inline
class PrepareGrammar(Transformer):
def terminal(self, name): def terminal(self, name):
return name return name
def nonterminal(self, name): def nonterminal(self, name):


+ 74
- 39
lark/transformers.py View File

@@ -1,7 +1,7 @@
import inspect import inspect
from functools import wraps from functools import wraps


from . import utils
from .utils import smart_decorator
from .tree import Tree from .tree import Tree


class Discard(Exception): class Discard(Exception):
@@ -13,46 +13,27 @@ class Base:
return getattr(self, tree.data, self.__default__)(tree) return getattr(self, tree.data, self.__default__)(tree)


def __default__(self, tree): def __default__(self, tree):
"Default operation on tree (for override)"
return tree return tree


class Transformer(Base): class Transformer(Base):
def _transform_children(self, children): def _transform_children(self, children):
for c in children: for c in children:
try: try:
yield self._transform(c) if isinstance(c, Tree) else c
yield self._transform_tree(c) if isinstance(c, Tree) else c
except Discard: except Discard:
pass pass


def _transform(self, tree):
def _transform_tree(self, tree):
tree = Tree(tree.data, list(self._transform_children(tree.children))) tree = Tree(tree.data, list(self._transform_children(tree.children)))
return self._call_userfunc(tree) return self._call_userfunc(tree)


def transform(self, tree): def transform(self, tree):
return self._transform(tree)
return self._transform_tree(tree)


def __mul__(self, other): def __mul__(self, other):
return TransformerChain(self, other) return TransformerChain(self, other)


class ChildrenTransformer(Transformer):
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 ChildrenInlineTransformer(Transformer):
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 TransformerChain(object): class TransformerChain(object):
def __init__(self, *transformers): def __init__(self, *transformers):
@@ -68,14 +49,22 @@ class TransformerChain(object):




class Transformer_InPlace(Transformer): class Transformer_InPlace(Transformer):
def _transform(self, tree):
def _transform_tree(self, tree): # Cancel recursion
return self._call_userfunc(tree) return self._call_userfunc(tree)


def transform(self, tree): def transform(self, tree):
for subtree in tree.iter_subtrees(): for subtree in tree.iter_subtrees():
subtree.children = list(self._transform_children(subtree.children)) subtree.children = list(self._transform_children(subtree.children))


return self._transform(tree)
return self._transform_tree(tree)


class Transformer_InPlaceRecursive(Transformer):
def _transform_tree(self, tree):
tree.children = list(self._transform_children(tree.children))
return self._call_userfunc(tree)




class Visitor(Base): class Visitor(Base):
"Bottom-up visitor" "Bottom-up visitor"
@@ -85,11 +74,6 @@ class Visitor(Base):
self._call_userfunc(subtree) self._call_userfunc(subtree)
return tree return tree


class Transformer_InPlaceRecursive(Transformer):
def _transform(self, tree):
tree.children = list(self._transform_children(tree.children))
return self._call_userfunc(tree)

class Visitor_Recursive(Base): class Visitor_Recursive(Base):
def visit(self, tree): def visit(self, tree):
for child in tree.children: for child in tree.children:
@@ -101,7 +85,6 @@ class Visitor_Recursive(Base):
return tree return tree




from functools import wraps
def visit_children_decor(func): def visit_children_decor(func):
@wraps(func) @wraps(func)
def inner(cls, tree): def inner(cls, tree):
@@ -126,11 +109,63 @@ class Interpreter(object):
return self.visit_children(tree) return self.visit_children(tree)




def inline_args(obj):
if inspect.isclass(obj) and issubclass(obj, ChildrenTransformer):
class _NewTransformer(ChildrenInlineTransformer, obj):
pass
return _NewTransformer
else:
return utils.inline_args(obj)


def _children_args__func(f):
@wraps(f)
def create_decorator(_f, with_self):
if with_self:
def f(self, tree):
return _f(self, tree.children)
else:
def f(args):
return _f(tree.children)

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


def children_args(obj):
decorator = _children_args__class if issubclass(obj, Base) else _children_args__func
return decorator(obj)



def _children_args_inline__func(f):
@wraps(f)
def create_decorator(_f, with_self):
if with_self:
def f(self, tree):
return _f(self, *tree.children)
else:
def f(args):
return _f(*tree.children)

return smart_decorator(f, 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 issubclass(obj, Base) else _children_args_inline__func
return decorator(obj)

+ 0
- 2
lark/tree.py View File

@@ -5,8 +5,6 @@ except ImportError:


from copy import deepcopy from copy import deepcopy


from .utils import inline_args

class Meta: class Meta:
pass pass




+ 5
- 15
lark/utils.py View File

@@ -50,22 +50,22 @@ except NameError: # Python 3
###{standalone ###{standalone


import types import types
import functools
from functools import wraps, partial
from contextlib import contextmanager from contextlib import contextmanager


Str = type(u'') Str = type(u'')


def smart_decorator(f, create_decorator): def smart_decorator(f, create_decorator):
if isinstance(f, types.FunctionType): if isinstance(f, types.FunctionType):
return functools.wraps(create_decorator(f, True))
return wraps(create_decorator(f, True))


elif isinstance(f, (type, types.BuiltinFunctionType)): elif isinstance(f, (type, types.BuiltinFunctionType)):
return functools.wraps(create_decorator(f, False))
return wraps(create_decorator(f, False))


elif isinstance(f, types.MethodType): elif isinstance(f, types.MethodType):
return functools.wraps(create_decorator(f.__func__, True))
return wraps(create_decorator(f.__func__, True))


elif isinstance(f, functools.partial):
elif isinstance(f, partial):
# wraps does not work for partials in 2.7: https://bugs.python.org/issue3445 # wraps does not work for partials in 2.7: https://bugs.python.org/issue3445
return create_decorator(f.__func__, True) return create_decorator(f.__func__, True)


@@ -73,16 +73,6 @@ def smart_decorator(f, create_decorator):
return create_decorator(f.__func__.__call__, True) return create_decorator(f.__func__.__call__, True)




def inline_args(f):
def create_decorator(_f, with_self):
if with_self:
def f(self, args):
return _f(self, *args)
else:
def f(args):
return _f(*args)

return smart_decorator(f, create_decorator)




try: try:


+ 2
- 2
tests/test_parser.py View File

@@ -21,8 +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.transformers import ChildrenTransformer as Transformer
# from lark.tree import Transformer
from lark.transformers import Transformer, children_args


__path__ = os.path.dirname(__file__) __path__ = os.path.dirname(__file__)
def _read(n, *args): def _read(n, *args):
@@ -94,6 +93,7 @@ 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>"


Loading…
Cancel
Save