1
0
Files
libcmo21/Assets/CodeGen/EnumsMigration/EnumsRender/template_render.py

195 lines
7.9 KiB
Python
Raw Normal View History

import jinja2
2026-01-27 16:38:29 +08:00
import jinja2.filters
import re
import typing
from json_loader import BEnumCollection, BEnum
from utils import CKParts
import utils
class RenderUtils:
2026-01-27 16:38:29 +08:00
"""Possible used functions for jinja when rendering templates"""
TRANTABLE_ESCAPE_STRING: typing.ClassVar[dict[int, str]] = str.maketrans({
"\\": "\\\\",
"\t": "\\t",
"\b": "\\b",
"\n": "\\n",
"\r": "\\r",
"\f": "\\f",
"\"": "\\\"",
})
@staticmethod
def escape_string(strl: str) -> str:
"""
Escape string
Escape all characters which are invalid in string quote.
:param strl: The string need to be escaped.
:return: The escaped string.
"""
return strl.translate(RenderUtils.TRANTABLE_ESCAPE_STRING)
TRANTABLE_REMOVE_EOL: typing.ClassVar[dict[int, str]] = str.maketrans({
"\n": "",
"\r": "",
})
@staticmethod
def remove_eol(strl: str) -> str:
"""
Remove EOL of given string.
When rendering code, adding line comment is a common case.
However, comment may have EOL.
So when adding this to line comment, we need to remove all EOL.
"""
return strl.translate(RenderUtils.TRANTABLE_REMOVE_EOL)
REGEX_PY_TO_LITERAL_NUMBER: typing.ClassVar[re.Pattern] = re.compile("[ulUL]+$")
@staticmethod
def to_py_num_literal(numstr: str) -> str:
2026-01-27 16:38:29 +08:00
"""
Convert given string into Python number literal style.
2026-01-27 16:38:29 +08:00
Number literal declaration in C++ is different with Python.
Previous one allow adding suffix like "UL" to indicate its type, but Python don't allow this.
So this function actually just remove "UL" suffix.
2026-01-27 16:38:29 +08:00
This function is only served for Python code generation.
:param numstr: The captured number.
:return: The Python style number string.
"""
return RenderUtils.REGEX_PY_TO_LITERAL_NUMBER.sub("", numstr, 1)
REGEX_PY_EXT_HUMANRDABLE_ENTRY_NAME: typing.ClassVar[re.Pattern] = re.compile("^[a-zA-Z0-9]+_")
@staticmethod
def underline_to_camel(entry_name: str) -> str:
2026-01-27 16:38:29 +08:00
"""
Convert given capital underlying name into camel name.
2026-01-27 16:38:29 +08:00
This function is only served for Python code generation.
As you noticed, almost entries of CK enums are fully capital and splitted by
underline. This is really not good for human reading, especially those who
are not programmer. So this function will try give these programmer-oriented
entry name a human readable name as its display name. However, this extract
method is not perfect. It simply do some split and replacement so the
generated content may still not good for reader.
:param entry_name: The name of enum entry
:return: A human readable entry name. No guaranteen that return value is must human readable.
"""
# remove first part (any content before underline '_')
entry_name = RenderUtils.REGEX_PY_EXT_HUMANRDABLE_ENTRY_NAME.sub("", entry_name, 1)
# then convert result into camel
return ' '.join(map(lambda s: s[0:1].upper() + s[1:].lower(), entry_name.split('_')))
class TemplateRender:
2026-01-27 16:38:29 +08:00
"""Render templates to code files"""
__loader: jinja2.BaseLoader
__environment: jinja2.Environment
def __init__(self) -> None:
self.__loader = jinja2.FileSystemLoader(utils.get_template_directory())
self.__environment = jinja2.Environment(loader=self.__loader)
2026-01-27 16:38:29 +08:00
# prepare filters
self.__environment.filters['some_or_blank'] = lambda s: "" if s is None else s
self.__environment.filters['escape_string'] = lambda s: RenderUtils.escape_string(s)
self.__environment.filters['block_comment'] = lambda s, fmt: jinja2.filters.do_indent(s, fmt, True, True)
self.__environment.filters['line_comment'] = lambda s: RenderUtils.remove_eol(s)
def __render(self, template_name: str, dest_filename: str, payload: BEnumCollection, extra: dict[str, typing.Any] = {}) -> None:
# prepare template argument
template_argument: dict[str, typing.Any] = {
'payload': payload,
'extra': extra,
'utils': RenderUtils,
}
# fetch template
template = self.__environment.get_template(str(template_name))
# render template and save
with open(utils.get_output_file_path(dest_filename), 'w', encoding='utf-8') as f:
f.write(template.render(**template_argument))
def __wrap_single_enum(self, benum: BEnum) -> BEnumCollection:
return BEnumCollection([benum, ])
# region: C++ Header and Sources
def render_cpp_enums(self, filename: str, benums: BEnumCollection) -> None:
self.__render('generic.hpp.jinja', filename, benums)
def render_cpp_enum(self, filename: str, benum: BEnum) -> None:
self.render_cpp_enums(filename, self.__wrap_single_enum(benum))
def render_cpp_enum_docstrings(self, hpp_filename: str, cpp_filename: str, benums: BEnumCollection, parts: CKParts) -> None:
self.__render('generic.docstring.hpp.jinja', hpp_filename, benums, {'parts': parts})
self.__render('generic.docstring.cpp.jinja', cpp_filename, benums, {'parts': parts})
def render_cpp_enum_docstring(self, hpp_filename: str, cpp_filename: str, benum: BEnum, parts: CKParts) -> None:
self.render_cpp_enum_docstrings(hpp_filename, cpp_filename, self.__wrap_single_enum(benum), parts)
def render_cpp_ckerror_docstring(self, hpp_filename: str, cpp_filename: str, benums: BEnum) -> None:
self.__render('CKERROR.docstring.hpp.jinja', hpp_filename, self.__wrap_single_enum(benums))
self.__render('CKERROR.docstring.cpp.jinja', cpp_filename, self.__wrap_single_enum(benums))
def render_cpp_ckclassid_docstring(self, hpp_filename: str, cpp_filename: str, benums: BEnum) -> None:
self.__render('CK_CLASSID.docstring.hpp.jinja', hpp_filename, self.__wrap_single_enum(benums))
self.__render('CK_CLASSID.docstring.cpp.jinja', cpp_filename, self.__wrap_single_enum(benums))
# endregion
# region: Python
def render_py_enums(self, filename: str, benums: BEnumCollection) -> None:
self.__render('generic.py.jinja', filename, benums)
def render_py_enum(self, filename: str, benum: BEnum) -> None:
self.render_py_enums(filename, self.__wrap_single_enum(benum))
def render_py_enum_docstrings(self, filename: str, benums: BEnumCollection) -> None:
self.__render('generic.docstring.py.jinja', filename, benums)
def render_py_enum_docstring(self, filename: str, benum: BEnum) -> None:
self.render_py_enum_docstrings(filename, self.__wrap_single_enum(benum))
# endregion
# region: C#
def render_cs_enums(self, filename: str, benums: BEnumCollection) -> None:
self.__render('generic.cs.jinja', filename, benums)
def render_cs_enum(self, filename: str, benum: BEnum) -> None:
self.render_cs_enums(filename, self.__wrap_single_enum(benum))
def render_cs_enum_docstrings(self, filename: str, benums: BEnumCollection) -> None:
self.__render('generic.docstring.cs.jinja', filename, benums)
def render_cs_enum_docstring(self, filename: str, benum: BEnum) -> None:
self.render_cs_enum_docstrings(filename, self.__wrap_single_enum(benum))
# endregion
# region: Rust
def render_rs_enums(self, filename: str, benums: BEnumCollection) -> None:
self.__render('generic.rs.jinja', filename, benums)
def render_rs_enum(self, filename: str, benum: BEnum) -> None:
self.render_rs_enums(filename, self.__wrap_single_enum(benum))
def render_rs_enum_docstrings(self, filename: str, benums: BEnumCollection) -> None:
self.__render('generic.docstring.rs.jinja', filename, benums)
def render_rs_enum_docstring(self, filename: str, benum: BEnum) -> None:
self.render_rs_enum_docstrings(filename, self.__wrap_single_enum(benum))
# endregion