diff --git a/muppet/puppet/ast.py b/muppet/puppet/ast.py index 2ce3c35263d793d5021e1345c9857d9b4f27ca2b..b527c23d1c8c1535a8fec20fd763c1d38046ef29 100644 --- a/muppet/puppet/ast.py +++ b/muppet/puppet/ast.py @@ -722,3 +722,107 @@ def build_ast(form: Any) -> Puppet: case default: logger.warning("Unhandled item: %a", default) return PuppetParseError(default) + + +def generate_template() -> None: + """ + Output code for a formater class. + + Each formatter needs to implement a rather large number of + methods, and keeping track of them all is hard. This generates a + ready-to-use template. + + .. code-block:: sh + + python -m muppet.puppet.ast > muppet/puppet/format/NAME.py + """ + + def pyind(level: int) -> str: + """Indent string for python code.""" + return ' ' * 4 * level + + subclasses = sorted(subclass.__name__ for subclass in Puppet.__subclasses__()) + + def tokenize_class(s: str) -> list[str]: + """Split a camel or pascal cased string into words.""" + out: list[str] = [] + current: str = '' + for c in s: + if c.isupper(): + if current.isupper(): + current += c + else: + out.append(current) + current = c + else: + current += c + out.append(current) + + if out[0] == '': + out = out[1:] + + return out + + def setup_override() -> None: + print(''' +from typing import ( + TypeVar, + Callable, +) + + +F = TypeVar('F', bound=Callable[..., object]) + +# TODO replace this decorator with +# from typing import override +# once the target python version is changed to 3.12 + + +def override(f: F) -> F: + """ + Return function unchanged. + + Placeholder @override annotator if the actual annotation isn't + implemented in the current python version. + """ + return f + + +'''.lstrip()) + + print('''""" +__ + +TODOFormatter +TODO_RETURN_TYPE +"""''') + + print() + print('import logging') + print('from .base import Serializer') + print('from muppet.puppet.ast import (') + for subclass in subclasses: + print(pyind(1) + subclass + ',') + print(')') + + setup_override() + + print() + print() + print('logger = logging.getLogger(__name__)') + print() + print() + + print('class TODOFormatter(Serializer[TODO_RETURN_TYPE]):') + print(pyind(1) + '"""TODO document me!!!"""') + for subclass in subclasses: + func_name = '_'.join(tokenize_class(subclass)).lower() + print() + print(pyind(1) + '@override') + print(pyind(1) + '@classmethod') + decl = f"def _{func_name}(cls, it: {subclass}, indent: int) -> TODO_RETURN_TYPE:" + print(pyind(1) + decl) + + +if __name__ == '__main__': + generate_template()