You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
60 lines
2.1 KiB
Python
60 lines
2.1 KiB
Python
"""
|
|
Module of utilities for transforming a lark.Tree into a custom Abstract Syntax Tree (AST defined in classes)
|
|
"""
|
|
|
|
import inspect, re
|
|
import types
|
|
from typing import Optional, Callable
|
|
|
|
from lark import Transformer, v_args
|
|
|
|
class Ast:
|
|
"""Abstract class
|
|
|
|
Subclasses will be collected by `create_transformer()`
|
|
"""
|
|
pass
|
|
|
|
class AsList:
|
|
"""Abstract class
|
|
|
|
Subclasses will be instantiated with the parse results as a single list, instead of as arguments.
|
|
"""
|
|
|
|
class WithMeta:
|
|
"""Abstract class
|
|
|
|
Subclasses will be instantiated 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: types.ModuleType,
|
|
transformer: Optional[Transformer]=None,
|
|
decorator_factory: Callable=v_args) -> Transformer:
|
|
"""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
|