#include "CodegenUtils.hpp" #include #include #include #include #include using namespace std::literals; bool Utils::WriteOutputFile(const CodegenOutput& output, const char* path) { auto outputFile = Utils::OpenCstdioFile(path, Utils::WriteTruncate); if (!outputFile) { printf("[ERROR] unable to open output file %s\n", path); return false; } DEFER { fclose(outputFile); }; DEBUG_PRINTF("Writing output %s\n", path); output.Write(outputFile); return true; } std::string Utils::JoinNames(DeclNamespace* ns, std::string_view prefix, std::string_view suffix, std::string_view delimiter) { size_t length = 0; if (!prefix.empty()) { length += prefix.length() + delimiter.length(); } if (!suffix.empty()) { length += suffix.length() + delimiter.length(); } size_t nsCount = 0; { DeclNamespace* curr = ns; while (curr) { length += curr->name.length() + delimiter.length(); curr = curr->container; ++nsCount; } } length -= delimiter.length(); std::string joined; joined.reserve(length); if (!prefix.empty()) { joined += prefix; joined += delimiter; } { DeclNamespace* curr = ns; size_t i = 0; while (curr) { joined += curr->name; if (!suffix.empty() || i != (nsCount - 1)) { joined += delimiter; } curr = curr->container; ++i; } } if (!suffix.empty()) { joined += suffix; } return joined; } std::string Utils::MakeFullName(std::string_view name, DeclNamespace* ns) { return JoinNames(ns, ""sv, name, "::"sv); } std::string Utils::MakeMangledName(std::string_view name, DeclNamespace* ns) { return JoinNames(ns, ""sv, name, "_"sv); } // NOTE: assuming we are only dealing with ASCII characters static bool IsLowerCase(char c) { return c >= 'a' && c <= 'z'; } static bool IsUpperCase(char c) { return c >= 'A' && c <= 'Z'; } static bool IsAlphabetic(char c) { return IsLowerCase(c) || IsUpperCase(c); } static char MakeUpperCase(char c) { if (IsAlphabetic(c)) { return IsUpperCase(c) ? c : ('A' + (c - 'a')); } return c; } std::vector Utils::SplitIdentifier(std::string_view name) { // TODO handle SCREAMING_CASE size_t chunkStart = 0; size_t chunkEnd = 0; std::vector result; auto PushChunk = [&]() { result.push_back(std::string_view(name.begin() + chunkStart, name.begin() + chunkEnd)); }; while (chunkEnd < name.size()) { char c = name[chunkEnd]; if (IsUpperCase(c)) { // Start of next chunk, using camelCase or PascalCase PushChunk(); chunkStart = chunkEnd; chunkEnd = chunkStart + 1; continue; } else if (c == '_') { // End of this chunk, using snake_case PushChunk(); chunkStart = chunkEnd + 1; chunkEnd = chunkStart + 1; continue; } else if (c == '-') { // End of this chunk, using kebab-case PushChunk(); chunkStart = chunkEnd + 1; chunkEnd = chunkStart + 1; continue; } ++chunkEnd; } if ((chunkEnd - chunkStart) >= 1) { PushChunk(); } return result; } std::string Utils::MakePascalCase(std::string_view name) { std::string result; for (auto part : SplitIdentifier(name)) { result += MakeUpperCase(part[0]); result += part.substr(1); } return result; } void Utils::ProduceGeneratedHeader(const char* headerFilename, CodegenOutput& header, const char* sourceFilename, CodegenOutput& source) { CodegenOutputThing headerOut; headerOut.text += &R"""( // This file is generated. Any changes will be overidden when building. #include #include #include )"""[1]; CodegenOutputThing sourceOut; APPEND_LIT_LN(sourceOut.text, "// This file is generated. Any changes will be overidden when building."); APPEND_FMT_LN(sourceOut.text, "#include \"%s\"", headerFilename); sourceOut.text += &R"""( #include #include #include using namespace std::literals; using namespace Metadata; )"""[1]; header.AddOutputThing(std::move(headerOut), 0); source.AddOutputThing(std::move(sourceOut), 0); }