"""
Module of utilities for transforming a lark.Tree into a custom Abstract Syntax Tree
"""
import inspect, re
from lark import Transformer, v_args
class Ast(object):
"""Abstract class
Subclasses will be collected by `create_transformer()`
"""
pass
class AsList(object):
"""Abstract class
Subclasses will be instanciated with the parse results as a single list, instead of as arguments.
"""
class WithMeta(object):
"""Abstract class
Subclasses will be instanciated with the Meta instance of the tree. (see ``v_args`` for more detail)
"""
pass
def camel_to_snake(name):
return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
def create_transformer(ast_module, transformer=None, decorator_factory=v_args):
"""Collects `Ast` subclasses from the given module, and creates a Lark transformer that builds the AST.
For each class, we create a corresponding rule in the transformer, with a matching name.
CamelCase names will be converted into snake_case. Example: "CodeBlock" -> "code_block".
Classes starting with an underscore (`_`) will be skipped.
Parameters:
ast_module: A Python module containing all the subclasses of ``ast_utils.Ast``
transformer (Optional[Transformer]): An initial transformer. Its attributes may be overwritten.
decorator_factory (Callable): An optional callable accepting two booleans, inline, and meta,
and returning a decorator for the methods of ``transformer``. (default: ``v_args``).
"""
t = transformer or Transformer()
for name, obj in inspect.getmembers(ast_module):
if not name.startswith('_') and inspect.isclass(obj):
if issubclass(obj, Ast):
wrapper = decorator_factory(inline=not issubclass(obj, AsList), meta=issubclass(obj, WithMeta))
obj = wrapper(obj).__get__(t)
setattr(t, camel_to_snake(name), obj)
return t