import re from dataclasses import dataclass, field from typing import List, ClassVar # taken from https://en.cppreference.com/w/cpp/keyword cpp_keywords = {"alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "char8_t", "char16_t", "char32_t", "class", "compl", "concept", "const", "consteval", "constexpr", "constinit", "const_cast", "continue", "co_await", "co_return", "co_yield", "decltype", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "reflexpr", "register", "reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", "this", "thread_local", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq"} _field_overrides = [ (re.compile(r"(start|end)_(line|column)|index|width|num_.*"), {"type": "unsigned"}), (re.compile(r"(.*)_"), lambda m: {"name": m[1]}), ] def get_field_override(field: str): for r, o in _field_overrides: m = r.fullmatch(field) if m: return o(m) if callable(o) else o return {} @dataclass class Field: name: str type: str is_optional: bool = False is_repeated: bool = False trap_name: str = None first: bool = False def __post_init__(self): if self.is_optional: self.type = f"std::optional<{self.type}>" if self.is_repeated: self.type = f"std::vector<{self.type}>" @property def cpp_name(self): if self.name in cpp_keywords: return self.name + "_" return self.name # using @property breaks pystache internals here def get_streamer(self): if self.type == "std::string": return lambda x: f"trapQuoted({x})" elif self.type == "bool": return lambda x: f'({x} ? "true" : "false")' else: return lambda x: x @property def is_single(self): return not (self.is_optional or self.is_repeated) @dataclass class Trap: table_name: str name: str fields: List[Field] id: Field = None def __post_init__(self): assert self.fields self.fields[0].first = True @dataclass class TagBase: base: str first: bool = False @dataclass class Tag: name: str bases: List[TagBase] index: int id: str def __post_init__(self): if self.bases: self.bases = [TagBase(b) for b in self.bases] self.bases[0].first = True @property def has_bases(self): return bool(self.bases) @dataclass class TrapList: template: ClassVar = 'trap_traps' traps: List[Trap] namespace: str trap_affix: str include_dir: str @dataclass class TagList: template: ClassVar = 'trap_tags' tags: List[Tag] namespace: str @dataclass class ClassBase: ref: 'Class' first: bool = False @dataclass class Class: name: str bases: List[ClassBase] = field(default_factory=list) final: bool = False fields: List[Field] = field(default_factory=list) trap_name: str = None def __post_init__(self): self.bases = [ClassBase(c) for c in sorted(self.bases, key=lambda cls: cls.name)] if self.bases: self.bases[0].first = True @property def has_bases(self): return bool(self.bases) @property def single_fields(self): return [f for f in self.fields if f.is_single] @dataclass class ClassList: template: ClassVar = "cpp_classes" classes: List[Class] namespace: str trap_affix: str include_dir: str