mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
This adds `cppgen`, creating structured C++ classes mirroring QL classes out of `schema.yml`. An example of generated code at the time of this commit can be found [in this gist][1]. [1]: https://gist.github.com/redsun82/57304ddb487a8aa40eaa0caa695048fa Closes https://github.com/github/codeql-c-team/issues/863
151 lines
4.0 KiB
Python
151 lines
4.0 KiB
Python
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|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}>"
|
|
elif 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] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class TagList:
|
|
template: ClassVar = 'trap_tags'
|
|
|
|
tags: List[Tag] = field(default_factory=list)
|
|
|
|
|
|
@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]
|