217 lines
6.8 KiB
C++
217 lines
6.8 KiB
C++
#include <cctype>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <optional>
|
|
#include <regex>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
enum class MetaCoreReflectedKind {
|
|
Struct,
|
|
Class,
|
|
Enum
|
|
};
|
|
|
|
struct MetaCoreReflectedField {
|
|
std::string Name{};
|
|
};
|
|
|
|
struct MetaCoreReflectedType {
|
|
MetaCoreReflectedKind Kind = MetaCoreReflectedKind::Struct;
|
|
std::string Name{};
|
|
std::vector<MetaCoreReflectedField> Fields{};
|
|
};
|
|
|
|
[[nodiscard]] std::string MetaCoreTrim(std::string value) {
|
|
auto isWhitespace = [](unsigned char character) {
|
|
return std::isspace(character) != 0;
|
|
};
|
|
|
|
while (!value.empty() && isWhitespace(static_cast<unsigned char>(value.front()))) {
|
|
value.erase(value.begin());
|
|
}
|
|
while (!value.empty() && isWhitespace(static_cast<unsigned char>(value.back()))) {
|
|
value.pop_back();
|
|
}
|
|
return value;
|
|
}
|
|
|
|
[[nodiscard]] std::string MetaCoreBuildIncludePath(const std::filesystem::path& path) {
|
|
const std::string full = path.generic_string();
|
|
constexpr std::string_view marker = "/Public/";
|
|
const std::size_t markerIndex = full.find(marker);
|
|
if (markerIndex == std::string::npos) {
|
|
return path.filename().generic_string();
|
|
}
|
|
return full.substr(markerIndex + marker.size());
|
|
}
|
|
|
|
[[nodiscard]] std::vector<MetaCoreReflectedType> MetaCoreParseHeader(const std::filesystem::path& path) {
|
|
std::ifstream input(path);
|
|
if (!input.is_open()) {
|
|
throw std::runtime_error("Unable to open header: " + path.string());
|
|
}
|
|
|
|
const std::regex typeRegex(R"(^(struct|class|enum\s+class)\s+([A-Za-z_]\w*))");
|
|
const std::regex fieldRegex(R"(^(.+?)\s+([A-Za-z_]\w*)\s*(?:\{.*\}|=.*)?;$)");
|
|
|
|
std::vector<MetaCoreReflectedType> reflectedTypes;
|
|
std::optional<MetaCoreReflectedKind> pendingKind;
|
|
MetaCoreReflectedType* currentType = nullptr;
|
|
bool expectingField = false;
|
|
|
|
std::string line;
|
|
while (std::getline(input, line)) {
|
|
const std::string trimmed = MetaCoreTrim(line);
|
|
if (trimmed.empty() || trimmed.starts_with("//")) {
|
|
continue;
|
|
}
|
|
|
|
if (trimmed.starts_with("MC_STRUCT")) {
|
|
pendingKind = MetaCoreReflectedKind::Struct;
|
|
continue;
|
|
}
|
|
if (trimmed.starts_with("MC_CLASS")) {
|
|
pendingKind = MetaCoreReflectedKind::Class;
|
|
continue;
|
|
}
|
|
if (trimmed.starts_with("MC_ENUM")) {
|
|
pendingKind = MetaCoreReflectedKind::Enum;
|
|
continue;
|
|
}
|
|
if (trimmed.starts_with("MC_PROPERTY")) {
|
|
expectingField = true;
|
|
continue;
|
|
}
|
|
if (trimmed.starts_with("MC_GENERATED_BODY")) {
|
|
continue;
|
|
}
|
|
|
|
if (pendingKind.has_value()) {
|
|
std::smatch match;
|
|
if (std::regex_search(trimmed, match, typeRegex)) {
|
|
reflectedTypes.push_back(MetaCoreReflectedType{
|
|
pendingKind.value(),
|
|
match[2].str(),
|
|
{}
|
|
});
|
|
currentType = &reflectedTypes.back();
|
|
pendingKind.reset();
|
|
expectingField = false;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (currentType != nullptr && trimmed == "};") {
|
|
currentType = nullptr;
|
|
expectingField = false;
|
|
continue;
|
|
}
|
|
|
|
if (currentType == nullptr || currentType->Kind == MetaCoreReflectedKind::Enum || !expectingField) {
|
|
continue;
|
|
}
|
|
|
|
std::smatch match;
|
|
if (!std::regex_search(trimmed, match, fieldRegex)) {
|
|
throw std::runtime_error("Unable to parse reflected field in " + path.string() + ": " + trimmed);
|
|
}
|
|
|
|
currentType->Fields.push_back(MetaCoreReflectedField{
|
|
match[2].str()
|
|
});
|
|
expectingField = false;
|
|
}
|
|
|
|
return reflectedTypes;
|
|
}
|
|
|
|
[[nodiscard]] std::string MetaCoreBuildOutput(
|
|
const std::string& functionName,
|
|
const std::vector<std::filesystem::path>& headers
|
|
) {
|
|
std::vector<MetaCoreReflectedType> reflectedTypes;
|
|
for (const auto& header : headers) {
|
|
const auto parsed = MetaCoreParseHeader(header);
|
|
reflectedTypes.insert(reflectedTypes.end(), parsed.begin(), parsed.end());
|
|
}
|
|
|
|
std::ostringstream output;
|
|
output << "#include \"MetaCoreFoundation/MetaCoreGeneratedReflection.h\"\n";
|
|
output << "#include \"MetaCoreFoundation/MetaCoreReflection.h\"\n";
|
|
for (const auto& header : headers) {
|
|
output << "#include \"" << MetaCoreBuildIncludePath(header) << "\"\n";
|
|
}
|
|
output << "\nnamespace MetaCore {\n\n";
|
|
output << "void " << functionName << "(MetaCoreTypeRegistry& registry) {\n";
|
|
|
|
for (const auto& reflectedType : reflectedTypes) {
|
|
if (reflectedType.Kind == MetaCoreReflectedKind::Enum) {
|
|
output << " MetaCoreRegisterGeneratedEnum<" << reflectedType.Name << ">(registry, \"" << reflectedType.Name << "\");\n";
|
|
continue;
|
|
}
|
|
|
|
output << " auto " << reflectedType.Name << "Builder = MetaCoreRegisterGeneratedStruct<"
|
|
<< reflectedType.Name << ">(registry, \"" << reflectedType.Name << "\");\n";
|
|
for (const auto& field : reflectedType.Fields) {
|
|
output << " " << reflectedType.Name << "Builder.Field<&" << reflectedType.Name << "::"
|
|
<< field.Name << ">(\"" << field.Name << "\");\n";
|
|
}
|
|
}
|
|
|
|
output << "}\n\n} // namespace MetaCore\n";
|
|
return output.str();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char** argv) {
|
|
std::filesystem::path outputPath;
|
|
std::string functionName;
|
|
std::vector<std::filesystem::path> headers;
|
|
|
|
for (int index = 1; index < argc; ++index) {
|
|
const std::string_view argument(argv[index]);
|
|
if (argument == "--output" && index + 1 < argc) {
|
|
outputPath = argv[++index];
|
|
continue;
|
|
}
|
|
if (argument == "--function" && index + 1 < argc) {
|
|
functionName = argv[++index];
|
|
continue;
|
|
}
|
|
if (argument == "--module" && index + 1 < argc) {
|
|
++index;
|
|
continue;
|
|
}
|
|
|
|
headers.emplace_back(argv[index]);
|
|
}
|
|
|
|
if (outputPath.empty() || functionName.empty() || headers.empty()) {
|
|
std::cerr << "Usage: MetaCoreHeaderTool --output <file> --function <name> [--module <name>] <headers...>\n";
|
|
return 1;
|
|
}
|
|
|
|
try {
|
|
const std::string output = MetaCoreBuildOutput(functionName, headers);
|
|
std::ofstream file(outputPath, std::ios::trunc);
|
|
if (!file.is_open()) {
|
|
std::cerr << "Unable to open output file: " << outputPath << '\n';
|
|
return 1;
|
|
}
|
|
file << output;
|
|
} catch (const std::exception& exception) {
|
|
std::cerr << exception.what() << '\n';
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|