aboutsummaryrefslogtreecommitdiff
path: root/source/20-codegen-compiler
diff options
context:
space:
mode:
Diffstat (limited to 'source/20-codegen-compiler')
-rw-r--r--source/20-codegen-compiler/CodegenConfig.hpp11
-rw-r--r--source/20-codegen-compiler/CodegenDecl.cpp74
-rw-r--r--source/20-codegen-compiler/CodegenDecl.hpp154
-rw-r--r--source/20-codegen-compiler/CodegenLexer.cpp202
-rw-r--r--source/20-codegen-compiler/CodegenLexer.hpp49
-rw-r--r--source/20-codegen-compiler/CodegenModel.cpp732
-rw-r--r--source/20-codegen-compiler/CodegenModel.hpp61
-rw-r--r--source/20-codegen-compiler/CodegenOutput.cpp39
-rw-r--r--source/20-codegen-compiler/CodegenOutput.hpp34
-rw-r--r--source/20-codegen-compiler/CodegenUtils.cpp171
-rw-r--r--source/20-codegen-compiler/CodegenUtils.hpp57
-rw-r--r--source/20-codegen-compiler/SQLiteHelper.hpp220
-rw-r--r--source/20-codegen-compiler/main.cpp1443
-rw-r--r--source/20-codegen-compiler/test/examples/TestClass.hpp.txt38
-rw-r--r--source/20-codegen-compiler/test/examples/TestEnum.hpp.txt44
15 files changed, 0 insertions, 3329 deletions
diff --git a/source/20-codegen-compiler/CodegenConfig.hpp b/source/20-codegen-compiler/CodegenConfig.hpp
deleted file mode 100644
index b9dc56c..0000000
--- a/source/20-codegen-compiler/CodegenConfig.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#ifndef CODEGEN_DEBUG_PRINT
-# define CODEGEN_DEBUG_PRINT 0
-#endif
-
-#if CODEGEN_DEBUG_PRINT
-# define DEBUG_PRINTF(...) printf(__VA_ARGS__)
-#else
-# define DEBUG_PRINTF(...)
-#endif
diff --git a/source/20-codegen-compiler/CodegenDecl.cpp b/source/20-codegen-compiler/CodegenDecl.cpp
deleted file mode 100644
index 11e1bb5..0000000
--- a/source/20-codegen-compiler/CodegenDecl.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "CodegenDecl.hpp"
-
-#include "CodegenUtils.hpp"
-
-#include <fmt/format.h>
-#include <Utils.hpp>
-
-const std::string& DeclStruct::GetMangledName() const {
- if (mangledName.empty()) {
- mangledName = Utils::MakeMangledName(name, container);
- }
- return mangledName;
-}
-
-std::string DeclXGlobalVar::MangleCtorName(std::string_view targetName) {
- return fmt::format("{}_MANGLED_ctor", targetName);
-}
-
-std::string DeclXGlobalVar::MangleDtorName(std::string_view targetName) {
- return fmt::format("{}_MANGLED_dtor", targetName);
-}
-
-const std::string& DeclEnum::GetMangledName() const {
- if (mangledName.empty()) {
- mangledName = Utils::MakeMangledName(name, container);
- }
- return mangledName;
-}
-
-static EnumValuePattern NextPattern(EnumValuePattern val) {
- return (EnumValuePattern)(val + 1);
-}
-
-EnumValuePattern DeclEnum::CalcPattern() const {
- if (elements.empty()) return EVP_Continuous;
-
- auto pattern = EVP_Continuous;
-restart:
- auto lastVal = elements[0].value;
- for (size_t i = 1; i < elements.size(); ++i) {
- auto currVal = elements[i].value;
- switch (pattern) {
- case EVP_Continuous: {
- bool satisfy = lastVal + 1 == currVal;
- if (!satisfy) {
- pattern = NextPattern(pattern);
- goto restart;
- }
- } break;
-
- case EVP_Bits: {
- bool satisfy = (lastVal << 1) == currVal;
- if (!satisfy) {
- pattern = NextPattern(pattern);
- goto restart;
- }
- } break;
-
- // A random pattern can match anything
- case EVP_Random:
- case EVP_COUNT: break;
- }
- lastVal = currVal;
- }
-
- return pattern;
-}
-
-EnumValuePattern DeclEnum::GetPattern() const {
- if (pattern == EVP_COUNT) {
- pattern = CalcPattern();
- }
- return pattern;
-}
diff --git a/source/20-codegen-compiler/CodegenDecl.hpp b/source/20-codegen-compiler/CodegenDecl.hpp
deleted file mode 100644
index f1ac5b1..0000000
--- a/source/20-codegen-compiler/CodegenDecl.hpp
+++ /dev/null
@@ -1,154 +0,0 @@
-#pragma once
-
-#include "CodegenOutput.hpp"
-
-#include <string>
-#include <vector>
-
-// TODO replace std::string name with std::string_view into the token storage?
-
-struct SourceFile {
- std::string filename;
- CodegenOutput preHeaderOutput;
- CodegenOutput postHeaderOutput;
- CodegenOutput postSourceOutput;
- CodegenOutput tuOutput; // "tu" = Translation Unit, produces a separately compiled .cpp file
- bool header = false;
- /// Whether this file is being reprocessed in this invocation of codegen.exe or not.
- bool reprocessing = false;
-};
-
-struct DeclNamespace {
- // NOTE: namespace doesn't have a source file field, because the same namespace can be "reopened" in multipled files
-
- DeclNamespace* container = nullptr;
- std::string name;
- const std::string* fullname = nullptr; // View into storage map key
-};
-
-struct DeclStruct;
-struct DeclMemberVariable {
- DeclStruct* containerStruct = nullptr;
- std::string name;
- std::string type;
- std::string getterName;
- std::string setterName;
- bool isGetterGenerated = false;
- bool isSetterGenerated = false;
-};
-struct DeclMemberFunction {
- DeclStruct* containerStruct = nullptr;
- // TODO
-};
-
-// Structs or classes
-struct DeclStruct {
- SourceFile* sourceFile = nullptr;
- DeclNamespace* container = nullptr;
- std::vector<const DeclStruct*> baseClasses;
- std::vector<DeclMemberVariable> memberVariables;
- std::vector<DeclMemberVariable> generatedVariables;
- std::vector<DeclMemberFunction> memberFunctions;
- std::vector<DeclMemberFunction> generatedFunctions;
- std::string name;
- mutable std::string mangledName;
- const std::string* fullname = nullptr; // View into storage map key
-
- // Scanned generation options
- bool generating : 1 = false;
- bool generatingInheritanceHiearchy : 1 = false;
-
- const std::string& GetName() const { return name; }
- const std::string& GetFullName() const { return *fullname; }
- const std::string& GetMangledName() const;
-};
-
-struct DeclXGlobalVar {
- std::string name;
- bool hasCtor = false;
- bool hasDtor = false;
-
- static std::string MangleCtorName(std::string_view targetName);
- std::string GetMangledCtorName() const { return MangleCtorName(name); }
- static std::string MangleDtorName(std::string_view targetName);
- std::string GetMangledDtorName() const { return MangleDtorName(name); }
-};
-
-enum EnumUnderlyingType {
- EUT_Int8,
- EUT_Int16,
- EUT_Int32,
- EUT_Int64,
- EUT_Uint8,
- EUT_Uint16,
- EUT_Uint32,
- EUT_Uint64,
- EUT_COUNT,
-};
-
-enum EnumValuePattern {
- // The numbers cover n..m with no gaps
- EVP_Continuous,
- // The numbers cover for i in n..m, 1 << i
- // e.g. [0] = 1 << 0,
- // [1] = 1 << 1.
- // [2] = 1 << 2. etc.
- EVP_Bits,
- // The numbesr don't have a particular pattern
- EVP_Random,
- EVP_COUNT,
-};
-
-struct DeclEnumElement {
- std::string name;
- // TODO support int64_t, etc. enum underlying types
- uint64_t value;
-};
-
-struct DeclEnum {
- SourceFile* sourceFile = nullptr;
- DeclNamespace* container = nullptr;
- std::string name;
- mutable std::string mangledName;
- const std::string* fullname = nullptr; // View into storage map key
- std::vector<DeclEnumElement> elements;
- EnumUnderlyingType underlyingType;
- // Start with invalid value, calculate on demand
- mutable EnumValuePattern pattern = EVP_COUNT;
-
- // TODO replace this with a regex?
- std::string generateRemovingPrefix;
- std::string generatingAddingPrefix;
- // NOTE: this flag acts as a gate for every specific generating option, must be enabled for them to work
- bool generating : 1 = false;
- bool generateToString : 1 = false;
- bool generateFromString : 1 = false;
- // NOTE: see GenerateForEnum() for the exact heuristics
- bool generateExcludeUseHeuristics : 1 = false;
-
- const std::string& GetName() const { return name; }
- const std::string& GetFullName() const { return *fullname; }
- const std::string& GetMangledName() const;
-
- std::string_view GetUnderlyingTypeName() const;
-
- EnumValuePattern CalcPattern() const;
- EnumValuePattern GetPattern() const;
-};
-
-struct DeclFunctionArgument {
- std::string type;
- std::string name;
-};
-
-struct DeclFunction {
- SourceFile* sourceFile = nullptr;
- DeclNamespace* container = nullptr;
- // Things like extern, static, etc. that gets written before the function return type
- std::string prefix;
- std::string name;
- const std::string* fullname = nullptr; // View into storage map key
- std::string returnType;
- std::vector<DeclFunctionArgument> arguments;
- std::string body;
-};
diff --git a/source/20-codegen-compiler/CodegenLexer.cpp b/source/20-codegen-compiler/CodegenLexer.cpp
deleted file mode 100644
index ecb2186..0000000
--- a/source/20-codegen-compiler/CodegenLexer.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-#include "CodegenLexer.hpp"
-
-#include <cassert>
-
-int StbLexerToken::Reamalgamate() const {
- if (type == CLEX_ext_single_char) {
- return text[0];
- } else {
- return type;
- }
-}
-
-bool StbTokenIsSingleChar(int lexerToken) {
- return lexerToken >= 0 && lexerToken < 256;
-}
-
-bool StbTokenIsMultiChar(int lexerToken) {
- return !StbTokenIsMultiChar(lexerToken);
-}
-
-std::string CombineTokens(std::span<const StbLexerToken> tokens, std::string_view separator) {
- if (tokens.empty()) {
- return {};
- }
-
- size_t length = 0;
- for (auto& token : tokens) {
- length += token.text.size();
- length += separator.size();
- }
- // Intentionally counting an extra separator: leave space for the last append below
-
- std::string result;
- result.reserve(length);
- for (auto& token : tokens) {
- result += token.text;
- result += separator;
- }
- // Remove the trailing separator
- result.resize(result.size() - separator.size());
-
- return result;
-}
-
-const StbLexerToken& CodegenLexer::Current() const {
- assert(idx < tokens.size());
- return tokens[idx];
-}
-
-void CodegenLexer::InitializeFrom(std::string_view source) {
- this->tokens = {};
- this->idx = 0;
-
- stb_lexer lexer;
- char stringStorage[65536];
- const char* srcBegin = source.data();
- const char* srcEnd = srcBegin + source.length();
- stb_c_lexer_init(&lexer, srcBegin, srcEnd, stringStorage, sizeof(stringStorage));
-
- struct TokenCombiningPattern {
- StbLexerToken result;
- char matchChars[16];
- };
-
- const TokenCombiningPattern kDoubleColon = {
- .result = {
- .text = "::",
- .type = CLEX_ext_double_colon,
- },
- .matchChars = { ':', ':', '\0' },
- };
- const TokenCombiningPattern kDotDotDot = {
- .result = {
- .text = "...",
- .type = CLEX_ext_dot_dot_dot,
- },
- .matchChars = { '.', '.', '.', '\0' },
- };
-
- const TokenCombiningPattern* currentState = nullptr;
- int currentStateCharIdx = 0;
-
- while (true) {
- // See stb_c_lexer.h's comments, here are a few additinos that aren't made clear in the file:
- // - `lexer->token` (noted as "token" below) after calling stb_c_lexer_get_token() contains either:
- // 1. 0 <= token < 256: an ASCII character (more precisely a single char that the lexer ate; technically can be an incomplete code unit)
- // 2. token < 0: an unknown token
- // 3. One of the `CLEX_*` enums: a special, recognized token such as an operator
-
- int stbToken = stb_c_lexer_get_token(&lexer);
- if (stbToken == 0) {
- // EOF
- break;
- }
-
- if (lexer.token == CLEX_parse_error) {
- printf("[ERROR] stb_c_lexer countered a parse error.\n");
- // TODO how to handle?
- continue;
- }
-
- StbLexerToken token;
- if (StbTokenIsSingleChar(lexer.token)) {
- char c = lexer.token;
-
- token.type = CLEX_ext_single_char;
- token.text = std::string(1, c);
-
- if (!currentState) {
-#define TRY_START_MATCH(states) \
- if (states.matchChars[0] == c) { \
- currentState = &states; \
- currentStateCharIdx = 1; \
- }
- TRY_START_MATCH(kDoubleColon);
- TRY_START_MATCH(kDotDotDot);
-#undef TRY_START_MATCH
- } else {
- if (currentState->matchChars[currentStateCharIdx] == c) {
- // Match success
- ++currentStateCharIdx;
-
- // If we matched all of the chars...
- if (currentState->matchChars[currentStateCharIdx] == '\0') {
- // We matched (currentStateCharIdx) tokens though this one is pushed into the vector, leaving (currentStateCharIdx - 1) tokens to be removed
- for (int i = 0, count = currentStateCharIdx - 1; i < count; ++i) {
- tokens.pop_back();
- }
-
- // Set the current token to desired result
- token = currentState->result;
-
- currentState = nullptr;
- currentStateCharIdx = 0;
- }
- } else {
- // Match fail, reset
-
- currentState = nullptr;
- currentStateCharIdx = 0;
- }
- }
- } else {
- token.type = lexer.token;
- // WORKAROUND: use null terminated string, stb_c_lexer doens't set string_len properly when parsing identifiers
- token.text = std::string(lexer.string);
-
- switch (token.type) {
- case CLEX_intlit:
- token.lexerIntNumber = lexer.int_number;
- break;
-
- case CLEX_floatlit:
- token.lexerRealNumber = lexer.real_number;
- break;
- }
- }
- tokens.push_back(std::move(token));
- token = {};
- }
-}
-
-const StbLexerToken* CodegenLexer::TryConsumeToken(int type) {
- auto& token = tokens[idx];
- if (token.type == type) {
- ++idx;
- return &token;
- }
- return nullptr;
-}
-
-const StbLexerToken* CodegenLexer::TryConsumeSingleCharToken(char c) {
- auto& token = tokens[idx];
- if (token.type == CLEX_ext_single_char &&
- token.text[0] == c)
- {
- ++idx;
- return &token;
- }
- return nullptr;
-}
-
-void CodegenLexer::SkipUntilToken(int type) {
- while (idx < tokens.size()) {
- if (Current().type == type) {
- break;
- }
- ++idx;
- }
-}
-
-void CodegenLexer::SkipUntilTokenSingleChar(char c) {
- while (idx < tokens.size()) {
- auto& curr = Current();
- if (curr.type == CLEX_ext_single_char &&
- curr.text[0] == c)
- {
- break;
- }
- ++idx;
- }
-}
diff --git a/source/20-codegen-compiler/CodegenLexer.hpp b/source/20-codegen-compiler/CodegenLexer.hpp
deleted file mode 100644
index ec8c8b7..0000000
--- a/source/20-codegen-compiler/CodegenLexer.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-#include <LookupTable.hpp>
-
-#include <stb_c_lexer.h>
-#include <span>
-#include <string>
-#include <string_view>
-#include <vector>
-
-enum {
- CLEX_ext_single_char = CLEX_first_unused_token,
- CLEX_ext_double_colon,
- CLEX_ext_dot_dot_dot,
- CLEX_ext_COUNT,
-};
-
-struct StbLexerToken {
- std::string text;
-
- union {
- double lexerRealNumber;
- long lexerIntNumber;
- };
-
- // Can either be CLEX_* or CLEX_ext_* values
- int type;
-
- int Reamalgamate() const;
-};
-
-bool StbTokenIsSingleChar(int lexerToken);
-bool StbTokenIsMultiChar(int lexerToken);
-std::string CombineTokens(std::span<const StbLexerToken> tokens, std::string_view separator = {});
-
-struct CodegenLexer {
- std::vector<StbLexerToken> tokens;
- size_t idx = 0;
-
- void InitializeFrom(std::string_view source);
-
- const StbLexerToken& Current() const;
-
- const StbLexerToken* TryConsumeToken(int type);
- const StbLexerToken* TryConsumeSingleCharToken(char c);
-
- void SkipUntilToken(int type);
- void SkipUntilTokenSingleChar(char c);
-};
diff --git a/source/20-codegen-compiler/CodegenModel.cpp b/source/20-codegen-compiler/CodegenModel.cpp
deleted file mode 100644
index 303ad4e..0000000
--- a/source/20-codegen-compiler/CodegenModel.cpp
+++ /dev/null
@@ -1,732 +0,0 @@
-#include "CodegenModel.hpp"
-
-#include "CodegenUtils.hpp"
-#include "SQLiteHelper.hpp"
-
-#include <Macros.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <robin_hood.h>
-#include <cassert>
-#include <cstdint>
-#include <stdexcept>
-#include <string>
-#include <variant>
-
-using namespace std::literals;
-
-// TODO only delete unused records from model instead of regenerating all records every time
-
-struct SomeDecl {
- std::variant<DeclStruct, DeclFunction, DeclEnum> v;
-};
-
-class CodegenRuntimeModel::Private {
- friend class CodegenArchiveModel;
-
-public:
- // We want address stability for everything
- robin_hood::unordered_node_map<std::string, SomeDecl, StringHash, StringEqual> decls;
- robin_hood::unordered_node_map<std::string, DeclNamespace, StringHash, StringEqual> namespaces;
-};
-
-// A number for `PRAGMA user_vesrion`, representing the current database version. Increment when the table format changes.
-#define CURRENT_DATABASE_VERSION 1
-constexpr int64_t kGlobalNamespaceId = 1;
-
-namespace {
-void PrintErrMsgIfPresent(char*& errMsg) {
- if (errMsg) {
- printf("SQLite error: %s\n", errMsg);
- sqlite3_free(errMsg);
- }
-}
-} // namespace
-
-class CodegenArchiveModel::Private {
- friend class CodegenRuntimeModel;
-
-public:
- // NOTE: this must be the first field, because we want it to destruct after all other statement fields
- SQLiteDatabase database;
- /* Core Statements */
- SQLiteStatement beginTransactionStmt;
- SQLiteStatement commitTransactionStmt;
- SQLiteStatement rollbackTransactionStmt;
- SQLiteStatement findFileStmt;
- SQLiteStatement storeFileStmt;
- SQLiteStatement findNamespaceStmt;
- SQLiteStatement getNamespaceStmt;
- SQLiteStatement storeNamespaceStmt;
- /* Component Statements, initalized on demand */
- SQLiteStatement storeStructStmt;
- SQLiteStatement storeStructBaseClassStmt;
- SQLiteStatement storeStructPropertyStmt;
- // TODO store method
- SQLiteStatement storeEnumStmt;
- SQLiteStatement storeEnumElmStmt;
- SQLiteStatement deleteFunctionDeclByFilenameStmt;
- SQLiteStatement deleteStructDeclByFilenameStmt;
- SQLiteStatement deleteEnumDeclByFilenameStmt;
- // TODO
- // SQLiteStatement getRootClassStmt;
-
- void InitializeDatabase() {
- char* errMsg = nullptr;
-
- int result = sqlite3_exec(database, "PRAGMA user_version = " STRINGIFY(CURRENT_DATABASE_VERSION), nullptr, nullptr, &errMsg);
- PrintErrMsgIfPresent(errMsg);
- assert(result == SQLITE_OK);
-
- // TODO unique with overloading, and container structs
- result = sqlite3_exec(database, R"""(
-BEGIN TRANSACTION;
-CREATE TABLE Files(
- -- NOTE: SQLite forbids foreign keys referencing the implicit `rowid` column, we have to create an alias for it
- Id INTEGER PRIMARY KEY,
- FileName TEXT,
- UNIQUE (FileName)
-);
-
-CREATE TABLE Namespaces(
- Id INTEGER PRIMARY KEY,
- ParentNamespaceId INTEGER REFERENCES Namespaces(Id),
- Name TEXT,
- UNIQUE (ParentNamespaceId, Name)
-);
-
-CREATE TABLE DeclFunctions(
- Id INTEGER PRIMARY KEY,
- FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE,
- NamespaceId INTEGER REFERENCES Namespaces(Id),
- Name TEXT
-);
-CREATE TABLE DeclFunctionParameters(
- FunctionId INTEGER REFERENCES DeclFunctions(Id) ON DELETE CASCADE,
- Name TEXT,
- Type TEXT,
- UNIQUE (FunctionId, Name)
-);
-
-CREATE TABLE DeclStructs(
- Id INTEGER PRIMARY KEY,
- FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE,
- NamespaceId INTEGER REFERENCES Namespaces(Id),
- Name TEXT,
- IsMetadataMarked INTEGER
-);
-CREATE TABLE DeclStructBaseClassRelations(
- StructId INTEGER REFERENCES DeclStructs(Id) ON DELETE CASCADE,
- -- NOTE: intentionally not foreign keys, because we want relations to still exist even if the base class is deleted
- -- we do validation after a complete regeneration pass, on reads
- ParentStructNamespaceId INTEGER,
- ParentStructName TEXT,
- UNIQUE (StructId, ParentStructNamespaceId, ParentStructName)
-);
-CREATE TABLE DeclStructProperties(
- StructId INTEGER REFERENCES DeclStructs(Id) ON DELETE CASCADE,
- Name TEXT,
- Type TEXT,
- -- NOTE: getter and setter may or may not be methods; search the DeclStructMethods table if needed
- GetterName TEXT,
- SetterName TEXT,
- IsPlainField INTEGER GENERATED ALWAYS AS (GetterName = '' AND SetterName = '') VIRTUAL,
- IsMetadataMarked INTEGER
-);
-CREATE TABLE DeclStructMethods(
- Id INTEGER PRIMARY KEY,
- StructId INTEGER REFERENCES DeclStructs(Id) ON DELETE CASCADE,
- Name TEXT,
- Type TEXT,
- IsConst INTEGER,
- IsMetadataMarked INTEGER
-);
-CREATE TABLE DeclStructMethodParameters(
- MethodId INTEGER REFERENCES DeclStructMethods(Id) ON DELETE CASCADE,
- Name TEXT,
- Type TEXT,
- UNIQUE (MethodId, Name)
-);
-
-CREATE TABLE DeclEnums(
- Id INTEGER PRIMARY KEY,
-FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE,
- NamespaceId INTEGER REFERENCES Namespaces(Id),
- Name TEXT,
- UnderlyingType TEXT
-);
-CREATE TABLE DeclEnumElements(
- EnumId INTEGER REFERENCES DeclEnums(Id) ON DELETE CASCADE,
- Name TEXT,
- Value INTEGER,
- UNIQUE (EnumId, Name)
-);
-
-CREATE INDEX Index_DeclFunctions_FileId ON DeclFunctions(FileId);
-CREATE INDEX Index_DeclStructs_FileId ON DeclStructs(FileId);
-CREATE INDEX Index_DeclEnums_FileId ON DeclEnums(FileId);
-
-CREATE UNIQUE INDEX Index_DeclFunctions_Identity ON DeclFunctions(NamespaceId, Name);
-
-CREATE UNIQUE INDEX Index_DeclStruct_Identity ON DeclStructs(NamespaceId, Name);
-CREATE UNIQUE INDEX Index_DeclStructProperties_Identity ON DeclStructProperties(StructId, Name);
-CREATE UNIQUE INDEX Index_DeclStructMethods_Identity ON DeclStructMethods(StructId, Name);
-
-CREATE UNIQUE INDEX Index_DeclEnums_Identity ON DeclEnums(NamespaceId, Name);
-
--- Special global namespace that has no parent, and Id should always be 1
-INSERT INTO Namespaces(Id, ParentNamespaceId, Name)
-VALUES (1, NULL, '<global namespace>');
-
-COMMIT TRANSACTION;
-)""",
- nullptr,
- nullptr,
- &errMsg);
- PrintErrMsgIfPresent(errMsg);
- assert(result == SQLITE_OK);
- }
-
- void BeginTransaction() {
- int result = sqlite3_step(beginTransactionStmt);
- assert(result == SQLITE_DONE);
- sqlite3_reset(beginTransactionStmt);
- }
-
- void CommitTransaction() {
- int result = sqlite3_step(commitTransactionStmt);
- assert(result == SQLITE_DONE);
- sqlite3_reset(commitTransactionStmt);
- }
-
- void RollbackTransaction() {
- int result = sqlite3_step(rollbackTransactionStmt);
- assert(result == SQLITE_DONE);
- sqlite3_reset(rollbackTransactionStmt);
- }
-
- /// \return Row ID of the namespace, or 0 if it currently doesn't exist.
- int64_t FindNamespace(const DeclNamespace* ns) {
- if (!ns) {
- return kGlobalNamespaceId;
- }
-
- return FindNamespaceImpl(*ns);
- }
-
- /// \return Row ID of the namespace.
- int64_t FindOrStoreNamespace(const DeclNamespace* ns) {
- if (!ns) {
- return kGlobalNamespaceId;
- }
-
- if (auto rowId = FindNamespaceImpl(*ns); rowId != 0) {
- return rowId;
- }
-
- SQLiteRunningStatement rt(storeNamespaceStmt);
- rt.BindArguments(FindOrStoreNamespace(ns->container), ns->name);
-
- rt.StepAndCheck(SQLITE_ROW);
-
- auto [nsId] = rt.ResultColumns<int64_t>();
- return nsId;
- }
-
- std::string GetNamespaceFullName(int64_t nsId) const {
- return GetNamespaceFullNameImpl(nsId, nullptr, 0);
- }
-
- std::string GetDeclFullName(int64_t nsId, std::string_view declName) const {
- return GetNamespaceFullNameImpl(nsId, declName.data(), declName.size());
- }
-
- /// \return Row ID of the file, or 0 if it currently doesn't exist.
- int64_t FindFile(std::string_view filename) {
- SQLiteRunningStatement rt(findFileStmt);
- rt.BindArguments(filename);
-
- int result = rt.Step();
-
- if (result == SQLITE_ROW) {
- auto [fileId] = rt.ResultColumns<int64_t>();
- return fileId;
- } else {
- return 0;
- }
- }
-
- /// \return Row ID of the file
- int64_t FindOrStoreFile(std::string_view filename) {
- if (auto id = FindFile(filename); id != 0) {
- return id;
- }
-
- SQLiteRunningStatement rt(storeFileStmt);
- rt.BindArguments(filename);
-
- rt.StepAndCheck(SQLITE_ROW);
-
- auto [fileId] = rt.ResultColumns<int64_t>();
- return fileId;
- }
-
- /// \return Row ID of the file, or 0 if not found.
- int64_t FindOrStoreFile(/*nullable*/ const SourceFile* file) {
- if (!file) {
- return 0;
- }
- return FindOrStoreFile(file->filename);
- }
-
-private:
- // TODO maybe merge with Utils::MakeFullName?
- std::string GetNamespaceFullNameImpl(int64_t nsId, const char* append, size_t appendLength) const {
- std::vector<std::string> namespaceNames;
- size_t fullnameLength = 0;
-
- sqlite3_stmt* stmt = getNamespaceStmt;
- int64_t currentNsId = nsId;
- while (true) {
- SQLiteRunningStatement rt(getNamespaceStmt);
- rt.BindArguments(currentNsId);
-
- rt.StepAndCheck(SQLITE_ROW);
-
- auto [id, parentNamespaceId, name] = rt.ResultColumns<int64_t, int64_t, std::string>();
- currentNsId = parentNamespaceId;
- fullnameLength += name.size() + 2;
- namespaceNames.push_back(std::move(name));
-
- if (parentNamespaceId == kGlobalNamespaceId) {
- break;
- }
- }
- if (append) {
- // Already has the '::' at the end
- fullnameLength += appendLength;
- } else {
- fullnameLength -= 2;
- }
-
- std::string fullname;
- fullname.reserve(fullnameLength);
-
- for (auto it = namespaceNames.rbegin(); it != namespaceNames.rend(); ++it) {
- fullname.append(*it);
- if (append || std::next(it) != namespaceNames.rend()) {
- fullname.append("::");
- }
- }
- if (append) {
- fullname += std::string_view(append, appendLength);
- }
-
- return fullname;
- }
-
- int64_t FindNamespaceImpl(const DeclNamespace& ns) {
- int64_t parentNsRowId;
- if (ns.container) {
- parentNsRowId = FindNamespaceImpl(*ns.container);
- if (parentNsRowId == 0) {
- // Parent namespace doesn't exist in database, shortcircuit
- return 0;
- }
- } else {
- parentNsRowId = kGlobalNamespaceId;
- }
-
- return FindNamespaceImpl(ns, parentNsRowId);
- }
-
- int64_t FindNamespaceImpl(const DeclNamespace& ns, int64_t parentNsRowId) {
- sqlite3_stmt* stmt = findNamespaceStmt;
- SQLiteRunningStatement rt(findNamespaceStmt);
- rt.BindArguments(parentNsRowId, ns.name);
-
- int result = rt.Step();
- if (result == SQLITE_ROW) {
- auto [nsId] = rt.ResultColumns<int64_t>();
- return nsId;
- } else {
- return 0;
- }
- }
-};
-
-CodegenRuntimeModel::CodegenRuntimeModel()
- : m{ new Private() } //
-{
-}
-
-CodegenRuntimeModel::~CodegenRuntimeModel() {
- delete m;
-}
-
-#define STORE_DECL_OF_TYPE(DeclType, fullname, decl) \
- auto [iter, success] = m->decls.try_emplace(std::move(fullname), SomeDecl{ .v = std::move(decl) }); \
- auto& key = iter->first; \
- auto& val = iter->second; \
- auto& declRef = std::get<DeclType>(val.v); \
- declRef.fullname = &key; \
- return &declRef
-
-DeclEnum* CodegenRuntimeModel::AddEnum(std::string fullname, DeclEnum decl) {
-#if CODEGEN_DEBUG_PRINT
- printf("Committed enum '%s'\n", decl.name.c_str());
- for (auto& elm : decl.elements) {
- printf(" - element %s = %" PRId64 "\n", elm.name.c_str(), elm.value);
- }
-#endif
-
- STORE_DECL_OF_TYPE(DeclEnum, fullname, decl);
-}
-
-DeclStruct* CodegenRuntimeModel::AddStruct(std::string fullname, DeclStruct decl) {
-#if CODEGEN_DEBUG_PRINT
- printf("Committed struct '%s'\n", decl.name.c_str());
- printf(" Base classes:\n");
- for (auto& base : decl.baseClasses) {
- printf(" - %.*s\n", PRINTF_STRING_VIEW(base->name));
- }
-#endif
-
- STORE_DECL_OF_TYPE(DeclStruct, fullname, decl);
-}
-
-#define FIND_DECL_OF_TYPE(DeclType) \
- auto iter = m->decls.find(name); \
- if (iter != m->decls.end()) { \
- auto& some = iter->second.v; \
- if (auto decl = std::get_if<DeclType>(&some)) { \
- return decl; \
- } \
- } \
- return nullptr
-
-const DeclEnum* CodegenRuntimeModel::FindEnum(std::string_view name) const {
- FIND_DECL_OF_TYPE(DeclEnum);
-}
-
-const DeclStruct* CodegenRuntimeModel::FindStruct(std::string_view name) const {
- FIND_DECL_OF_TYPE(DeclStruct);
-}
-
-DeclNamespace* CodegenRuntimeModel::AddNamespace(DeclNamespace ns) {
- auto path = Utils::MakeFullName(""sv, &ns);
- auto [iter, success] = m->namespaces.try_emplace(std::move(path), std::move(ns));
- auto& nsRef = iter->second;
- if (success) {
- nsRef.fullname = &iter->first;
- }
- return &nsRef;
-}
-
-const DeclNamespace* CodegenRuntimeModel::FindNamespace(std::string_view fullname) const {
- auto iter = m->namespaces.find(fullname);
- if (iter != m->namespaces.end()) {
- return &iter->second;
- } else {
- return nullptr;
- }
-}
-
-DeclNamespace* CodegenRuntimeModel::FindNamespace(std::string_view name) {
- return const_cast<DeclNamespace*>(const_cast<const CodegenRuntimeModel*>(this)->FindNamespace(name));
-}
-
-CodegenArchiveModel::CodegenArchiveModel(std::string_view dbPath)
- : m{ new Private() } //
-{
- std::string zstrPath(dbPath);
- int reuslt = sqlite3_open(zstrPath.c_str(), &m->database);
- if (reuslt != SQLITE_OK) {
- std::string msg;
- msg += "Failed to open SQLite3 database, error message:\n";
- msg += sqlite3_errmsg(m->database);
- throw std::runtime_error(msg);
- }
-
- // NOTE: These pragmas are not persistent, so we need to set them every time
- // As of SQLite3 3.38.5, it defaults to foreign_keys = OFF, so we need this to be on for ON DELETE CASCADE and etc. to work
- sqlite3_exec(m->database, "PRAGMA foreign_keys = ON", nullptr, nullptr, nullptr);
- // This database is used for a buildsystem and can be regenerated at any time. We don't care for the slightest about data integrity, we just want fast updates
- sqlite3_exec(m->database, "PRAGMA synchronous = OFF", nullptr, nullptr, nullptr);
- sqlite3_exec(m->database, "PRAGMA journal_mode = MEMORY", nullptr, nullptr, nullptr);
-
- {
- SQLiteStatement readVersionStmt;
- readVersionStmt.InitializeLazily(m->database, "PRAGMA user_version"sv);
-
- int result = sqlite3_step(readVersionStmt);
- assert(result == SQLITE_ROW);
- int currentDatabaseVersion = sqlite3_column_int(readVersionStmt, 0);
-
- result = sqlite3_step(readVersionStmt);
- assert(result == SQLITE_DONE);
-
- if (currentDatabaseVersion == 0) {
- // Newly created database, initialize it
- m->InitializeDatabase();
- } else if (currentDatabaseVersion == CURRENT_DATABASE_VERSION) {
- // Same version, no need to do anything
- } else {
- INPLACE_FMT(msg, "Incompatbile database versions %d (in file) vs %d (expected).", currentDatabaseVersion, CURRENT_DATABASE_VERSION);
- throw std::runtime_error(msg);
- }
- }
-
- // Initialize core statements
- m->beginTransactionStmt.Initialize(m->database, "BEGIN TRANSACTION");
- m->commitTransactionStmt.Initialize(m->database, "COMMIT TRANSACTION");
- m->rollbackTransactionStmt.Initialize(m->database, "ROLLBACK TRANSACTION");
- m->findFileStmt.Initialize(m->database, "SELECT Id FROM Files WHERE FileName = ?1");
- m->storeFileStmt.Initialize(m->database, "INSERT INTO Files(FileName) VALUES (?1) RETURNING Id");
- m->findNamespaceStmt.Initialize(m->database, "SELECT Id FROM Namespaces WHERE ParentNamespaceId = ?1 AND Name = ?2");
- m->getNamespaceStmt.Initialize(m->database, "SELECT * FROM Namespaces WHERE Id = ?1"sv);
- m->storeNamespaceStmt.Initialize(m->database, "INSERT INTO Namespaces(ParentNamespaceId, Name) VALUES (?1, ?2) RETURNING Id");
-}
-
-CodegenArchiveModel::~CodegenArchiveModel() {
- delete m;
-}
-
-void CodegenArchiveModel::DeleteDeclsRelatedToFile(std::string_view filename) {
- // -Argument- -Description-
- // ?1 The filename to delete
- m->deleteFunctionDeclByFilenameStmt.InitializeLazily(m->database, "DELETE FROM DeclFunctions WHERE FileId = (SELECT Id FROM Files WHERE FileName = ?1)"sv);
- m->deleteStructDeclByFilenameStmt.InitializeLazily(m->database, "DELETE FROM DeclStructs WHERE FileId = (SELECT Id FROM Files WHERE FileName = ?1);"sv);
- m->deleteEnumDeclByFilenameStmt.InitializeLazily(m->database, "DELETE FROM DeclEnums WHERE FileId = (SELECT Id FROM Files WHERE FileName = ?1);"sv);
-
- m->BeginTransaction();
- auto stmtList = {
- m->deleteFunctionDeclByFilenameStmt.stmt,
- m->deleteStructDeclByFilenameStmt.stmt,
- m->deleteEnumDeclByFilenameStmt.stmt,
- };
- for (auto& stmt : stmtList) {
- SQLiteRunningStatement rt(stmt);
- rt.BindArguments(filename);
- rt.StepUntilDone();
- }
- m->CommitTransaction();
-}
-
-void CodegenArchiveModel::Store(const CodegenRuntimeModel& cgModel) {
- auto& cgm = cgModel.GetPimpl();
-
- struct Visiter {
- CodegenArchiveModel* self;
-
- void operator()(const DeclStruct& decl) const {
- self->StoreStruct(decl);
- }
- void operator()(const DeclFunction& decl) const {
- self->StoreFunction(decl);
- }
- void operator()(const DeclEnum& decl) const {
- self->StoreEnum(decl);
- }
- } visiter;
- visiter.self = this;
-
- m->BeginTransaction();
-
- for (auto&& [DISCARD, ns] : cgm.namespaces) {
- // This will insert the namespace if it doesn't exist, or no-op (fetches data) if it already exists
- m->FindOrStoreNamespace(&ns);
- }
- for (auto&& [DISCARD, value] : cgm.decls) {
- std::visit(visiter, value.v);
- }
-
- m->CommitTransaction();
-}
-
-void CodegenArchiveModel::LoadInto(CodegenRuntimeModel& model) const {
- // TODO
-}
-
-CodegenRuntimeModel CodegenArchiveModel::Load() const {
- CodegenRuntimeModel cgModel;
-
- // TODO files
- // TODO namespaces
-
- robin_hood::unordered_map<int64_t, DeclStruct*> structsById;
- robin_hood::unordered_map<int64_t, DeclMemberVariable*> propertiesById;
- robin_hood::unordered_map<int64_t, DeclMemberFunction*> methodsById;
-
- { // Load structs
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructs"sv);
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- auto [id, fileId, nsId, name] = rt.ResultColumns<int64_t, int64_t, int64_t, std::string_view>();
-
- auto decl = cgModel.AddStruct(m->GetDeclFullName(nsId, name), DeclStruct{});
- structsById.try_emplace(id, decl);
- }
- }
- { // Load struct's base classes
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructBaseClassRelations");
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- auto [structId, parentStructNsId, parentStructName] = rt.ResultColumns<int64_t, int64_t, std::string_view>();
-
- auto declThis = structsById.at(structId);
- auto declParent = cgModel.FindStruct(parentStructName); // TODO namespace
- declThis->baseClasses.push_back(declParent);
- }
- }
- { // Load struct properties
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructProperties"sv);
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- // TODO
- }
- }
- { // Load struct methods
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethods"sv);
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- // TODO
- }
- }
- { // Load method params
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethodParameters"sv);
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- // TODO
- }
- }
-
- return cgModel;
-}
-
-void CodegenArchiveModel::StoreStruct(const DeclStruct& decl) {
- // -Argument- -Description-
- // ?1 Namespace ID
- // ?2 Struct name
- // ?3 File ID containing the struct
- // ?4 Is this struct marked for metadata generation?
- m->storeStructStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclStructs(NamespaceId, Name, FileId, IsMetadataMarked)
-VALUES (?1, ?2, ?3, ?4)
-ON CONFLICT DO UPDATE SET
- FileId = ?3,
- IsMetadataMarked = ?4
-RETURNING Id
-)"""sv);
-
- // -Argument- -Description-
- // ?1 Struct ID
- // ?2 Parent struct's namespace ID
- // ?3 Parent struct's name
- m->storeStructBaseClassStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclStructBaseClassRelations(StructId, ParentStructNamespaceId, ParentStructName)
-VALUES (?1, ?2, ?3)
-)"""sv);
-
- // -Argument- -Description-
- // ?1 Struct ID
- // ?2 Property name
- // ?3 Property type
- // ?4 Getter name (optional)
- // ?5 Setter name (optional)
- // ?6 Is this property marked for metadata generation?
- m->storeStructPropertyStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclStructProperties(StructId, Name, Type, GetterName, SetterName, IsMetadataMarked)
-VALUES (?1, ?2, ?3, ?4, ?5, ?6)
-)"""sv);
-
- SQLiteRunningStatement rt(m->storeStructStmt);
- rt.BindArguments(m->FindOrStoreNamespace(decl.container), decl.name, m->FindOrStoreFile(decl.sourceFile), decl.generating);
- rt.StepAndCheck(SQLITE_ROW);
- auto [structId] = rt.ResultColumns<int64_t>();
-
- for (auto& baseClass : decl.baseClasses) {
- SQLiteRunningStatement rt(m->storeStructBaseClassStmt);
- rt.BindArguments(structId, m->FindOrStoreNamespace(baseClass->container), baseClass->name);
- rt.StepUntilDone();
- }
-
- for (auto& property : decl.memberVariables) {
- SQLiteRunningStatement rt(m->storeStructPropertyStmt);
- rt.BindArguments(
- structId,
- property.name,
- property.type,
- property.getterName,
- property.setterName,
- // Since DeclMemberVariable entries currently only exist if it's marked BRUSSEL_PROPERTY
- true);
- rt.StepUntilDone();
- }
-
- for (auto& method : decl.memberFunctions) {
- // TODO
- }
-}
-
-void CodegenArchiveModel::StoreFunction(const DeclFunction& decl) {
- // TODO
-}
-
-void CodegenArchiveModel::StoreEnum(const DeclEnum& decl) {
- // -Argument- -Description-
- // ?1 Namespace ID
- // ?2 Enum name
- // ?3 Enum underlying type
- // ?4 File ID containing the enum
- m->storeEnumStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclEnums(NamespaceId, Name, UnderlyingType, FileId)
-VALUES (?1, ?2, ?3, ?4)
-ON CONFLICT DO UPDATE SET
- UnderlyingType = ?3,
- FileId = ?4
-RETURNING Id
-)"""sv);
-
- // -Argument- -Description-
- // ?1 Container enum's id
- // ?2 Enum element name
- // ?3 Enum element value
- m->storeEnumElmStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclEnumElements(EnumId, Name, Value)
-VALUES (?1, ?2, ?3)
-ON CONFLICT DO UPDATE SET Value=?3
-)"""sv);
-
- SQLiteRunningStatement rt(m->storeEnumStmt);
- rt.BindArguments(m->FindOrStoreNamespace(decl.container), decl.name, decl.GetUnderlyingTypeName(), m->FindOrStoreFile(decl.sourceFile));
- rt.StepAndCheck(SQLITE_ROW);
- auto [enumId] = rt.ResultColumns<int64_t>();
-
- for (auto& elm : decl.elements) {
- SQLiteRunningStatement rt(m->storeEnumElmStmt);
- rt.BindArguments(enumId, elm.name, elm.value);
- rt.StepUntilDone();
- }
-}
diff --git a/source/20-codegen-compiler/CodegenModel.hpp b/source/20-codegen-compiler/CodegenModel.hpp
deleted file mode 100644
index 99c345d..0000000
--- a/source/20-codegen-compiler/CodegenModel.hpp
+++ /dev/null
@@ -1,61 +0,0 @@
-#pragma once
-
-#include "CodegenConfig.hpp"
-#include "CodegenDecl.hpp"
-#include "CodegenModel.hpp"
-#include "CodegenUtils.hpp"
-
-#include <sqlite3.h>
-#include <cinttypes>
-#include <cstdint>
-#include <string>
-#include <string_view>
-
-using namespace std::literals;
-
-class CodegenRuntimeModel {
-private:
- class Private;
- Private* m;
-
-public:
- CodegenRuntimeModel();
- ~CodegenRuntimeModel();
-
- // Implementation detail helper, don't use outside
- Private& GetPimpl() const { return *m; }
-
- DeclEnum* AddEnum(std::string fullname, DeclEnum decl);
- DeclStruct* AddStruct(std::string fullname, DeclStruct decl);
-
- const DeclEnum* FindEnum(std::string_view name) const;
- const DeclStruct* FindStruct(std::string_view name) const;
-
- DeclNamespace* AddNamespace(DeclNamespace ns);
-
- const DeclNamespace* FindNamespace(std::string_view fullname) const;
- DeclNamespace* FindNamespace(std::string_view name);
-};
-
-class CodegenArchiveModel {
-private:
- class Private;
- Private* m;
-
-public:
- CodegenArchiveModel(std::string_view dbPath);
- ~CodegenArchiveModel();
-
- // Implementation detail helper, don't use outside
- Private& GetPimpl() const { return *m; }
-
- void DeleteDeclsRelatedToFile(std::string_view filename);
-
- void Store(const CodegenRuntimeModel& model);
- void LoadInto(CodegenRuntimeModel& model) const;
- CodegenRuntimeModel Load() const;
-
- void StoreStruct(const DeclStruct& decl);
- void StoreFunction(const DeclFunction& decl);
- void StoreEnum(const DeclEnum& decl);
-};
diff --git a/source/20-codegen-compiler/CodegenOutput.cpp b/source/20-codegen-compiler/CodegenOutput.cpp
deleted file mode 100644
index d85feac..0000000
--- a/source/20-codegen-compiler/CodegenOutput.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "CodegenOutput.hpp"
-
-#include "CodegenUtils.hpp"
-
-void CodegenOutput::AddRequestInclude(std::string_view include) {
- if (!mRequestIncludes.contains(include)) {
- mRequestIncludes.insert(std::string(include));
- }
-}
-
-void CodegenOutput::AddOutputThing(CodegenOutputThing thing, int placementLocation) {
- if (placementLocation < 0 || placementLocation >= mOutThings.size()) {
- mOutThings.push_back(std::move(thing));
- } else {
- int maxIndex = (int)mOutThings.size() - 1;
- if (placementLocation > maxIndex) {
- placementLocation = maxIndex;
- }
-
- auto placementIter = mOutThings.begin() + placementLocation;
- mOutThings.insert(placementIter, std::move(thing));
- }
-}
-
-void CodegenOutput::MergeContents(CodegenOutput other) {
- std::move(other.mOutThings.begin(), other.mOutThings.end(), std::back_inserter(this->mOutThings));
-}
-
-void CodegenOutput::Write(FILE* file) const {
- for (auto& include : mRequestIncludes) {
- // TODO how to resolve to the correct include paths?
- WRITE_FMT_LN(file, "#include <%s>", include.c_str());
- }
-
- for (auto& thing : mOutThings) {
- fwrite(thing.text.c_str(), sizeof(char), thing.text.size(), file);
- WRITE_LIT(file, "\n");
- }
-}
diff --git a/source/20-codegen-compiler/CodegenOutput.hpp b/source/20-codegen-compiler/CodegenOutput.hpp
deleted file mode 100644
index df949f5..0000000
--- a/source/20-codegen-compiler/CodegenOutput.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <Utils.hpp>
-
-#include <robin_hood.h>
-#include <algorithm>
-#include <cstdio>
-#include <cstdlib>
-#include <string>
-#include <vector>
-
-// A generic "thing" (could be anything, comments, string-concated functionsm, etc.) to spit into the output file
-struct CodegenOutputThing {
- std::string text;
-};
-
-class CodegenOutput {
-private:
- robin_hood::unordered_set<std::string, StringHash, StringEqual> mRequestIncludes;
- std::vector<CodegenOutputThing> mOutThings;
-
-public:
- std::string optionOutPrefix;
- // Whether to add prefixes mOutPrefix to all global names or not
- bool optionAutoAddPrefix : 1 = false;
-
-public:
- void AddRequestInclude(std::string_view include);
- void AddOutputThing(CodegenOutputThing thing, int placementLocation = -1);
-
- void MergeContents(CodegenOutput other);
-
- void Write(FILE* file) const;
-};
diff --git a/source/20-codegen-compiler/CodegenUtils.cpp b/source/20-codegen-compiler/CodegenUtils.cpp
deleted file mode 100644
index 5bc5d79..0000000
--- a/source/20-codegen-compiler/CodegenUtils.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-#include "CodegenUtils.hpp"
-
-#include <Macros.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <cstdio>
-#include <cstdlib>
-
-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<std::string_view> Utils::SplitIdentifier(std::string_view name) {
- // TODO handle SCREAMING_CASE
-
- size_t chunkStart = 0;
- size_t chunkEnd = 0;
- std::vector<std::string_view> 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 <MetadataBase.hpp>
-#include <cstddef>
-#include <cstdint>
-)"""[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 <frozen/string.h>
-#include <frozen/unordered_map.h>
-#include <MetadataDetails.hpp>
-using namespace std::literals;
-using namespace Metadata;
-)"""[1];
-
- header.AddOutputThing(std::move(headerOut), 0);
- source.AddOutputThing(std::move(sourceOut), 0);
-}
diff --git a/source/20-codegen-compiler/CodegenUtils.hpp b/source/20-codegen-compiler/CodegenUtils.hpp
deleted file mode 100644
index 2d5b684..0000000
--- a/source/20-codegen-compiler/CodegenUtils.hpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#include "CodegenConfig.hpp"
-#include "CodegenDecl.hpp"
-#include "CodegenOutput.hpp"
-
-#include <algorithm>
-#include <string>
-#include <string_view>
-
-// I give up, hopefully nothing overflows this buffer
-// TODO handle buffer sizing properly
-
-#define INPLACE_FMT(varName, format, ...) \
- char varName[2048]; \
- snprintf(varName, sizeof(varName), format, __VA_ARGS__);
-
-#define APPEND_LIT(out, str) \
- out += str
-
-#define APPEND_FMT(out, format, ...) \
- { \
- char buffer[65536]; \
- snprintf(buffer, sizeof(buffer), format, __VA_ARGS__); \
- out += buffer; \
- }
-
-#define WRITE_LIT(file, str) \
- fwrite(str, sizeof(char), sizeof(str) - 1, file)
-
-// NOTE: snprintf() returns the size written (given an infinite buffer) not including \0
-#define WRITE_FMT(file, format, ...) \
- { \
- char buffer[65536]; \
- int size = snprintf(buffer, sizeof(buffer), format, __VA_ARGS__); \
- fwrite(buffer, sizeof(char), std::min<int>(size, sizeof(buffer)), file); \
- }
-
-#define APPEND_LIT_LN(out, str) APPEND_LIT(out, (str "\n"))
-#define APPEND_FMT_LN(out, format, ...) APPEND_FMT(out, (format "\n"), __VA_ARGS__)
-#define WRITE_LIT_LN(out, str) WRITE_LIT(out, (str "\n"))
-#define WRITE_FMT_LN(out, format, ...) WRITE_FMT(out, (format "\n"), __VA_ARGS__)
-
-namespace Utils {
-
-bool WriteOutputFile(const CodegenOutput& output, const char* path);
-
-std::string JoinNames(DeclNamespace* ns, std::string_view prefix, std::string_view suffix, std::string_view delimiter);
-std::string MakeFullName(std::string_view name, DeclNamespace* ns = nullptr);
-std::string MakeMangledName(std::string_view name, DeclNamespace* ns = nullptr);
-
-std::vector<std::string_view> SplitIdentifier(std::string_view name);
-std::string MakePascalCase(std::string_view name);
-
-void ProduceGeneratedHeader(const char* headerFilename, CodegenOutput& header, const char* sourceFilename, CodegenOutput& source);
-
-} // namespace Utils
diff --git a/source/20-codegen-compiler/SQLiteHelper.hpp b/source/20-codegen-compiler/SQLiteHelper.hpp
deleted file mode 100644
index c24e476..0000000
--- a/source/20-codegen-compiler/SQLiteHelper.hpp
+++ /dev/null
@@ -1,220 +0,0 @@
-#pragma once
-
-#include <fmt/format.h>
-#include <sqlite3.h>
-#include <cassert>
-#include <chrono>
-#include <cstddef>
-#include <cstdint>
-#include <sstream>
-#include <stdexcept>
-#include <string_view>
-#include <tuple>
-#include <type_traits>
-
-struct SQLiteDatabase {
- sqlite3* database = nullptr;
-
- ~SQLiteDatabase() {
- // NOTE: calling with NULL is a harmless no-op
- int result = sqlite3_close(database);
- assert(result == SQLITE_OK);
- }
-
- operator sqlite3*() const { return database; }
- sqlite3** operator&() { return &database; }
-};
-
-struct SQLiteStatement {
- sqlite3_stmt* stmt = nullptr;
-
- SQLiteStatement(const SQLiteStatement&) = delete;
- SQLiteStatement& operator=(const SQLiteStatement&) = delete;
-
- SQLiteStatement() = default;
-
- SQLiteStatement(sqlite3* database, std::string_view sql) {
- Initialize(database, sql);
- }
-
- ~SQLiteStatement() {
- // NOTE: calling with NULL is a harmless no-op
- // NOTE: we don't care about the error code, because they are returned if the statement has errored in the most recent execution
- // but deleting it will succeeed anyways
- sqlite3_finalize(stmt);
- }
-
- operator sqlite3_stmt*() const { return stmt; }
- sqlite3_stmt** operator&() { return &stmt; }
-
- void Initialize(sqlite3* database, std::string_view sql) {
- int result = sqlite3_prepare_v2(database, sql.data(), sql.size(), &stmt, nullptr);
- if (result != SQLITE_OK) {
- auto msg = fmt::format(
- "Failed to prepare SQLite3 statement, error message: {}",
- sqlite3_errmsg(sqlite3_db_handle(stmt)));
- throw std::runtime_error(msg);
- }
- }
-
- bool InitializeLazily(sqlite3* database, std::string_view sql) {
- if (!stmt) {
- Initialize(database, sql);
- return true;
- }
- return false;
- }
-};
-
-struct SQLiteRunningStatement {
- sqlite3_stmt* stmt;
-
- SQLiteRunningStatement(sqlite3_stmt* stmt)
- : stmt{ stmt } {
- }
-
- SQLiteRunningStatement(const SQLiteStatement& stmt)
- : stmt{ stmt.stmt } {
- }
-
- ~SQLiteRunningStatement() {
- sqlite3_clear_bindings(stmt);
- sqlite3_reset(stmt);
- }
-
- void BindArgument(int index, int32_t value) {
- sqlite3_bind_int(stmt, index, (int)value);
- }
-
- void BindArgument(int index, uint32_t value) {
- sqlite3_bind_int(stmt, index, (int)value);
- }
-
- void BindArgument(int index, int64_t value) {
- sqlite3_bind_int64(stmt, index, value);
- }
-
- void BindArgument(int index, uint64_t value) {
- sqlite3_bind_int64(stmt, index, (int64_t)value);
- }
-
- void BindArgument(int index, const char* value) {
- sqlite3_bind_text(stmt, index, value, -1, nullptr);
- }
-
- void BindArgument(int index, std::string_view value) {
- sqlite3_bind_text(stmt, index, value.data(), value.size(), nullptr);
- }
-
- void BindArgument(int index, std::nullptr_t) {
- // Noop
- }
-
- template <typename... Ts>
- void BindArguments(Ts&&... args) {
- // NOTE: SQLite3 argument index starts at 1
- size_t idx = 1;
- auto HandleEachArgument = [this, &idx]<typename T>(T&& arg) {
- BindArgument(idx, std::forward<T>(arg));
- ++idx;
- };
- (HandleEachArgument(std::forward<Ts>(args)), ...);
- }
-
- int Step() {
- return sqlite3_step(stmt);
- }
-
- void StepAndCheck(int forErr) {
- int err = sqlite3_step(stmt);
- assert(err == forErr);
- }
-
- int StepAndCheckError() {
- int err = sqlite3_step(stmt);
- if (err != SQLITE_DONE || err != SQLITE_ROW) {
- auto msg = fmt::format(
- "Error {} executing SQLite3 statement, error message: {}",
- sqlite3_errstr(err),
- sqlite3_errmsg(sqlite3_db_handle(stmt)));
- throw std::runtime_error(msg);
- }
- return err;
- }
-
- void StepUntilDone() {
- while (true) {
- int err = sqlite3_step(stmt);
- // SQLITE_OK is never returned for sqlite3_step() //TODO fact check this
- if (err == SQLITE_DONE) {
- break;
- }
- if (err == SQLITE_ROW) {
- continue;
- }
-
- auto msg = fmt::format(
- "Error {} executing SQLite3 statement, error message: {}",
- sqlite3_errstr(err),
- sqlite3_errmsg(sqlite3_db_handle(stmt)));
- throw std::runtime_error(msg);
- }
- }
-
- using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
- using TpFromUnixTimestamp = std::pair<TimePoint, int64_t>;
- using TpFromDateTime = std::pair<TimePoint, const char*>;
-
- // TODO replace with overloads?
- template <typename T>
- auto ResultColumn(int column) const {
- if constexpr (std::is_enum_v<T>) {
- auto value = sqlite3_column_int64(stmt, column);
- return static_cast<T>(value);
- } else if constexpr (std::is_same_v<T, int> || std::is_same_v<T, bool>) {
- return (T)sqlite3_column_int(stmt, column);
- } else if constexpr (std::is_same_v<T, int64_t>) {
- return (T)sqlite3_column_int64(stmt, column);
- } else if constexpr (std::is_same_v<T, const char*>) {
- return (const char*)sqlite3_column_text(stmt, column);
- } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
- // SQLite3 uses `unsigned char` to represent UTF-8 code units, on all platforms we care about this is the same as plain `char`
- auto cstr = (const char*)sqlite3_column_text(stmt, column);
- // For std::string_view, this finds size based on null terminator and stores reference to pointer
- // For std::string, this also allocates buffer and copies `cstr` content
- return T(cstr);
- } else if constexpr (std::is_same_v<T, TpFromUnixTimestamp>) {
- auto unixTimestamp = sqlite3_column_int64(stmt, column);
- auto chrono = std::chrono::seconds(unixTimestamp);
- return TimePoint(chrono);
- } else if constexpr (std::is_same_v<T, TpFromDateTime>) {
- // TODO wait for libstdc++ and libc++ implement c++20 std::chrono addition
-#ifdef _MSC_VER
- auto datetime = (const char*)sqlite3_column_text(stmt, column);
- if (datetime) {
- std::stringstream ss(datetime);
- TimePoint timepoint;
- ss >> std::chrono::parse("%F %T", timepoint);
- return timepoint;
- } else {
- return TimePoint();
- }
-#else
- static_assert(false && sizeof(T), "Unimplemented");
-#endif
- } else {
- static_assert(false && sizeof(T), "Unknown type");
- }
- }
-
- template <typename... Ts>
- auto ResultColumns() {
- // NOTE: SQLite3 column index starts at 0
- // NOTE: ((size_t)-1) + 1 == 0
- size_t idx = -1;
- // NOTE: std::make_tuple() -- variadic template function
- // std::tuple() -- CTAD constructor
- // Both of these cause make the comma operator unsequenced, not viable here
- return std::tuple{ (++idx, ResultColumn<Ts>(idx))... };
- }
-};
diff --git a/source/20-codegen-compiler/main.cpp b/source/20-codegen-compiler/main.cpp
deleted file mode 100644
index a2e50f5..0000000
--- a/source/20-codegen-compiler/main.cpp
+++ /dev/null
@@ -1,1443 +0,0 @@
-#include "CodegenConfig.hpp"
-#include "CodegenDecl.hpp"
-#include "CodegenLexer.hpp"
-#include "CodegenModel.hpp"
-#include "CodegenOutput.hpp"
-#include "CodegenUtils.hpp"
-
-#include <Enum.hpp>
-#include <LookupTable.hpp>
-#include <Macros.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <robin_hood.h>
-#include <stb_c_lexer.h>
-#include <cinttypes>
-#include <cstdlib>
-#include <filesystem>
-#include <memory>
-#include <optional>
-#include <span>
-#include <string>
-#include <string_view>
-
-using namespace std::literals;
-namespace fs = std::filesystem;
-
-// TODO support codegen target in .cpp files
-// TOOD maybe switch to libclang, maintaining this parser is just too painful
-
-struct AppState {
- CodegenRuntimeModel* runtimeModel;
- CodegenArchiveModel* archiveModel;
- // NOTE: decl objects reference the SourceFile objects by pointer
- robin_hood::unordered_node_map<std::string, SourceFile, StringHash, StringEqual> sourceFiles;
- std::vector<DeclEnum*> enumsToRevisit;
- std::vector<DeclStruct*> structsToRevisit;
- std::string_view outputDir;
- std::string_view databaseFilePath;
-
- SourceFile& GetOrCreateSourceFile(std::string_view filename) {
- auto iter = sourceFiles.find(filename);
- if (iter != sourceFiles.end()) {
- return iter->second;
- } else {
- auto [iter, success] = sourceFiles.try_emplace(std::string(filename), SourceFile{});
- // NOTE: "persistent" means pointer stable below
- auto& persistentFilename = iter->first;
- auto& persistentSourceFile = iter->second;
- persistentSourceFile.filename = persistentFilename;
- return persistentSourceFile;
- }
- }
-};
-
-FSTR_LUT_DECL(ClexNames, CLEX_eof, CLEX_ext_COUNT) {
- FSTR_LUT_MAP_FOR(ClexNames);
- FSTR_LUT_MAP_ENUM(CLEX_intlit);
- FSTR_LUT_MAP_ENUM(CLEX_floatlit);
- FSTR_LUT_MAP_ENUM(CLEX_id);
- FSTR_LUT_MAP_ENUM(CLEX_dqstring);
- FSTR_LUT_MAP_ENUM(CLEX_sqstring);
- FSTR_LUT_MAP_ENUM(CLEX_charlit);
- FSTR_LUT_MAP_ENUM(CLEX_eq);
- FSTR_LUT_MAP_ENUM(CLEX_noteq);
- FSTR_LUT_MAP_ENUM(CLEX_lesseq);
- FSTR_LUT_MAP_ENUM(CLEX_greatereq);
- FSTR_LUT_MAP_ENUM(CLEX_andand);
- FSTR_LUT_MAP_ENUM(CLEX_oror);
- FSTR_LUT_MAP_ENUM(CLEX_shl);
- FSTR_LUT_MAP_ENUM(CLEX_shr);
- FSTR_LUT_MAP_ENUM(CLEX_plusplus);
- FSTR_LUT_MAP_ENUM(CLEX_minusminus);
- FSTR_LUT_MAP_ENUM(CLEX_pluseq);
- FSTR_LUT_MAP_ENUM(CLEX_minuseq);
- FSTR_LUT_MAP_ENUM(CLEX_muleq);
- FSTR_LUT_MAP_ENUM(CLEX_diveq);
- FSTR_LUT_MAP_ENUM(CLEX_modeq);
- FSTR_LUT_MAP_ENUM(CLEX_andeq);
- FSTR_LUT_MAP_ENUM(CLEX_oreq);
- FSTR_LUT_MAP_ENUM(CLEX_xoreq);
- FSTR_LUT_MAP_ENUM(CLEX_arrow);
- FSTR_LUT_MAP_ENUM(CLEX_eqarrow);
- FSTR_LUT_MAP_ENUM(CLEX_shleq);
- FSTR_LUT_MAP_ENUM(CLEX_shreq);
- FSTR_LUT_MAP_ENUM(CLEX_ext_single_char);
- FSTR_LUT_MAP_ENUM(CLEX_ext_double_colon);
- FSTR_LUT_MAP_ENUM(CLEX_ext_dot_dot_dot);
-}
-
-FSTR_LUT_DECL(EnumUnderlyingType, 0, EUT_COUNT) {
- FSTR_LUT_MAP_FOR(EnumUnderlyingType);
- FSTR_LUT_MAP(EUT_Int8, "int8_t");
- FSTR_LUT_MAP(EUT_Int16, "int16_t");
- FSTR_LUT_MAP(EUT_Int32, "int32_t");
- FSTR_LUT_MAP(EUT_Int64, "int64_t");
- FSTR_LUT_MAP(EUT_Uint8, "uint8_t");
- FSTR_LUT_MAP(EUT_Uint16, "uint16_t");
- FSTR_LUT_MAP(EUT_Uint32, "uint32_t");
- FSTR_LUT_MAP(EUT_Uint64, "uint64_t");
-}
-
-RSTR_LUT_DECL(EnumUnderlyingType, 0, EUT_COUNT) {
- RSTR_LUT_MAP_FOR(EnumUnderlyingType);
-
- // Platform-dependent types
- RSTR_LUT_MAP(EUT_Int16, "short");
- RSTR_LUT_MAP(EUT_Int16, "short int");
- RSTR_LUT_MAP(EUT_Uint16, "unsigned short");
- RSTR_LUT_MAP(EUT_Uint16, "unsigned short int");
- RSTR_LUT_MAP(EUT_Int32, "int");
- RSTR_LUT_MAP(EUT_Uint32, "unsigned");
- RSTR_LUT_MAP(EUT_Uint32, "unsigned int");
-#ifdef _WIN32
- RSTR_LUT_MAP(EUT_Int32, "long");
- RSTR_LUT_MAP(EUT_Int32, "long int");
- RSTR_LUT_MAP(EUT_Uint32, "unsigned long");
- RSTR_LUT_MAP(EUT_Uint32, "unsigned long int");
-#else
- RSTR_LUT_MAP(EUT_Int64, "long");
- RSTR_LUT_MAP(EUT_Int64, "long int");
- RSTR_LUT_MAP(EUT_Uint64, "unsigned long");
- RSTR_LUT_MAP(EUT_Uint64, "unsigned long int");
-#endif
- RSTR_LUT_MAP(EUT_Int64, "long long");
- RSTR_LUT_MAP(EUT_Int64, "long long int");
- RSTR_LUT_MAP(EUT_Uint64, "unsigned long long");
- RSTR_LUT_MAP(EUT_Uint64, "unsigned long long int");
-
- // Sized types
- RSTR_LUT_MAP(EUT_Int8, "int8_t");
- RSTR_LUT_MAP(EUT_Int16, "int16_t");
- RSTR_LUT_MAP(EUT_Int32, "int32_t");
- RSTR_LUT_MAP(EUT_Int64, "int64_t");
- RSTR_LUT_MAP(EUT_Uint8, "uint8_t");
- RSTR_LUT_MAP(EUT_Uint16, "uint16_t");
- RSTR_LUT_MAP(EUT_Uint32, "uint32_t");
- RSTR_LUT_MAP(EUT_Uint64, "uint64_t");
-}
-
-FSTR_LUT_DECL(EnumValuePattern, 0, EVP_COUNT) {
- FSTR_LUT_MAP_FOR(EnumValuePattern);
- FSTR_LUT_MAP_ENUM(EVP_Continuous);
- FSTR_LUT_MAP_ENUM(EVP_Bits);
- FSTR_LUT_MAP_ENUM(EVP_Random);
-}
-
-enum CppKeyword {
- CKw_Namespace,
- CKw_Struct,
- CKw_Class,
- CKw_Enum,
- CKw_Public,
- CKw_Protected,
- CKw_Private,
- CKw_Virtual,
- CKw_Using,
- CKw_Template,
- CKw_COUNT,
-};
-
-RSTR_LUT_DECL(CppKeyword, 0, CKw_COUNT) {
- RSTR_LUT_MAP_FOR(CppKeyword);
- RSTR_LUT_MAP(CKw_Namespace, "namespace");
- RSTR_LUT_MAP(CKw_Struct, "struct");
- RSTR_LUT_MAP(CKw_Class, "class");
- RSTR_LUT_MAP(CKw_Enum, "enum");
- RSTR_LUT_MAP(CKw_Public, "public");
- RSTR_LUT_MAP(CKw_Protected, "protected");
- RSTR_LUT_MAP(CKw_Private, "private");
- RSTR_LUT_MAP(CKw_Virtual, "virtual");
- RSTR_LUT_MAP(CKw_Using, "using");
- RSTR_LUT_MAP(CKw_Template, "template");
-}
-
-enum CodegenDirective {
- CD_Class,
- CD_ClassProperty,
- CD_ClassMethod,
- CD_Enum,
- CD_XGlobalVar,
- CD_XGlobalVarCtor,
- CD_XGlobalVarDtor,
- CD_COUNT,
-};
-
-RSTR_LUT_DECL(CodegenDirective, 0, CD_COUNT) {
- RSTR_LUT_MAP_FOR(CodegenDirective);
- RSTR_LUT_MAP(CD_Class, "BRUSSEL_CLASS");
- RSTR_LUT_MAP(CD_ClassProperty, "BRUSSEL_PROPERTY");
- RSTR_LUT_MAP(CD_ClassMethod, "BRUSSEL_METHOD");
- RSTR_LUT_MAP(CD_Enum, "BRUSSEL_ENUM");
- RSTR_LUT_MAP(CD_XGlobalVar, "BRUSSEL_GLOBAL_DECL");
- RSTR_LUT_MAP(CD_XGlobalVarCtor, "BRUSSEL_GLOBAL_CTOR");
- RSTR_LUT_MAP(CD_XGlobalVarDtor, "BRUSSEL_GLOBAL_DTOR");
-}
-
-std::vector<std::vector<const StbLexerToken*>>
-TryConsumeDirectiveArgumentList(CodegenLexer& lexer) {
- std::vector<std::vector<const StbLexerToken*>> result;
- decltype(result)::value_type currentArg;
-
- size_t i = lexer.idx;
- int parenDepth = 0;
- for (; i < lexer.tokens.size(); ++i) {
- auto& token = lexer.tokens[i];
- if (token.text[0] == '(') {
- if (parenDepth > 0) {
- currentArg.push_back(&token);
- }
- ++parenDepth;
- } else if (token.text[0] == ')') {
- --parenDepth;
- if (parenDepth == 0) {
- // End of argument list
- ++i; // Consume the ')' token
- break;
- }
- } else if (parenDepth > 0) {
- // Parse these only if we are inside the argument list
- if (token.text[0] == ',') {
- result.push_back(std::move(currentArg));
- currentArg = {};
- } else {
- currentArg.push_back(&token);
- }
- }
- }
-
- if (!currentArg.empty()) {
- result.push_back(std::move(currentArg));
- }
-
- lexer.idx = i;
- return result;
-}
-
-bool TryConsumeKeyword(CodegenLexer& lexer, CppKeyword keyword) {
- auto& token = lexer.Current();
- if (token.type == CLEX_id) {
- auto iter = RSTR_LUT(CppKeyword).find(token.text);
- if (iter != RSTR_LUT(CppKeyword).end()) {
- ++lexer.idx;
- return true;
- }
- }
- return false;
-}
-
-bool TryConsumeAnyKeyword(CodegenLexer& lexer) {
- auto& token = lexer.Current();
- if (token.type == CLEX_id &&
- RSTR_LUT(CppKeyword).contains(token.text))
- {
- ++lexer.idx;
- return true;
- }
- return false;
-}
-
-std::optional<DeclMemberVariable>
-TryConsumeMemberVariable(CodegenLexer& lexer) {
- // The identifier/name will always be one single token, right before the 1st '=' (if has initializer) or ';' (no initializer)
- // NOTE: we assume there is no (a == b) stuff in the templates
-
- auto& tokens = lexer.tokens;
- auto& idx = lexer.idx;
-
- size_t idenTokIdx;
- size_t typeStart = idx;
- size_t typeEnd;
- for (; idx < tokens.size(); ++idx) {
- auto& token = tokens[idx];
- if (token.type == CLEX_ext_single_char) {
- if (token.text[0] == '=') {
- typeEnd = idx - 1;
- idenTokIdx = idx - 1;
- lexer.SkipUntilTokenSingleChar(';');
- goto found;
- } else if (token.text[0] == ';') {
- typeEnd = idx - 1;
- idenTokIdx = idx - 1;
- goto found;
- }
- }
- }
- // We reached end of input but still no end of statement
- return {};
-
-found:
- if (tokens[idenTokIdx].type != CLEX_id) {
- // Expected identifier, found something else
- return {};
- }
-
- DeclMemberVariable result;
- result.name = tokens[idenTokIdx].text;
- result.type = CombineTokens(std::span(&tokens[typeStart], &tokens[typeEnd]));
-
- // Consume the '=' or ';' token
- ++idx;
-
- return result;
-}
-
-EnumUnderlyingType TryConsumeEnumUnderlyingType(CodegenLexer& lexer) {
- // Try 1, 2, 3, 4 tokens from the current position
- // NOTE: see the FSTR map initialization code for reference that there is max 4 tokens
- for (int i = 4; i >= 1; --i) {
- auto text = CombineTokens(std::span(&lexer.Current(), i), " "sv);
- auto iter = RSTR_LUT(EnumUnderlyingType).find(text);
- if (iter != RSTR_LUT(EnumUnderlyingType).end()) {
- lexer.idx += i;
- return iter->second;
- }
- }
- return EUT_COUNT;
-}
-
-// Also includes the ':' token in the front
-EnumUnderlyingType TryConsumeEnumUnderlyingTypeClause(CodegenLexer& lexer) {
- if (lexer.Current().text != ":") {
- return EUT_COUNT;
- }
-
- ++lexer.idx;
- return TryConsumeEnumUnderlyingType(lexer);
-}
-
-enum StructMetaGenOptions {
- SMGO_InheritanceHiearchy,
- SMGO_COUNT,
-};
-
-RSTR_LUT_DECL(StructMetaGenOptions, 0, SMGO_COUNT) {
- RSTR_LUT_MAP_FOR(StructMetaGenOptions);
- RSTR_LUT_MAP(SMGO_InheritanceHiearchy, "InheritanceHiearchy");
-}
-
-enum StructPropertyOptions {
- SPO_Getter,
- SPO_Setter,
- SPO_COUNT,
-};
-
-RSTR_LUT_DECL(StructPropertyOptions, 0, SPO_COUNT) {
- RSTR_LUT_MAP_FOR(StructPropertyOptions);
- RSTR_LUT_MAP(SPO_Getter, "GETTER");
- RSTR_LUT_MAP(SPO_Setter, "SETTER");
-}
-
-enum EnumMetaGenOptions {
- EMGO_ToString,
- EMGO_FromString,
- EMGO_ExcludeUseHeuristics,
- EMGO_RemovePrefix,
- EMGO_AddPrefix,
- EMGO_COUNT,
-};
-
-RSTR_LUT_DECL(EnumMetaGenOptions, 0, EMGO_COUNT) {
- RSTR_LUT_MAP_FOR(EnumMetaGenOptions);
- RSTR_LUT_MAP(EMGO_ToString, "ToString");
- RSTR_LUT_MAP(EMGO_FromString, "FromString");
- RSTR_LUT_MAP(EMGO_ExcludeUseHeuristics, "ExcludeHeuristics");
- RSTR_LUT_MAP(EMGO_RemovePrefix, "RemovePrefix");
- RSTR_LUT_MAP(EMGO_AddPrefix, "AddPrefix");
-}
-
-void GenerateEnumStringArray(CodegenOutput& out, const DeclEnum& decl, const char* arrayName, const std::vector<DeclEnumElement>& filteredElements) {
- CodegenOutputThing thing;
- APPEND_FMT_LN(thing.text, "const char* %s[] = {", arrayName);
- for (auto& elm : filteredElements) {
- APPEND_FMT_LN(thing.text, "\"%s\",", elm.name.c_str());
- }
- APPEND_LIT_LN(thing.text, "};");
- out.AddOutputThing(std::move(thing));
-}
-
-void GenerateEnumStringMap(CodegenOutput& out, const DeclEnum& decl, const char* mapName, const std::vector<DeclEnumElement>& filteredElements) {
- CodegenOutputThing thing;
- // TODO
- out.AddOutputThing(std::move(thing));
-}
-
-void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const DeclEnum& decl) {
- auto& enumName = decl.GetFullName();
- auto& mangledName = decl.GetMangledName();
-
- auto useExcludeHeuristics = decl.generateExcludeUseHeuristics;
- auto filteredElements = [&]() {
- if (useExcludeHeuristics) {
- decltype(decl.elements) result;
- for (auto& elm : decl.elements) {
- if (elm.name.ends_with("COUNT")) continue;
-
- std::string_view trimmedName = elm.name;
- if (!decl.generateRemovingPrefix.empty() &&
- elm.name.starts_with(decl.generateRemovingPrefix))
- {
- trimmedName = trimmedName.substr(decl.generateRemovingPrefix.size());
- }
-
- result.push_back(DeclEnumElement{
- .name = decl.generatingAddingPrefix + std::string(trimmedName),
- .value = elm.value,
- });
- }
- return result;
- } else {
- return decl.elements;
- }
- }();
-
- if (decl.generateToString) {
- // Generate value -> string lookup table and function
- INPLACE_FMT(val2StrName, "gCG_%s_Val2Str", mangledName.c_str());
-
- switch (decl.GetPattern()) {
- case EVP_Continuous: {
- GenerateEnumStringArray(sourceOut, decl, val2StrName, filteredElements);
- int minVal = filteredElements.empty() ? 0 : filteredElements.front().value;
- int maxVal = filteredElements.empty() ? 0 : filteredElements.back().value;
-
- CodegenOutputThing lookupFunctionDecl;
- {
- auto& o = lookupFunctionDecl.text;
- APPEND_LIT_LN(o, "template <>");
- APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value);", enumName.c_str(), enumName.c_str());
- }
-
- CodegenOutputThing lookupFunctionDef;
- {
- auto& o = lookupFunctionDef.text;
- APPEND_LIT_LN(o, "template <>");
- APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value) {", enumName.c_str(), enumName.c_str());
- APPEND_FMT_LN(o, " auto intVal = (%s)value;", FSTR_LUT_LOOKUP(EnumUnderlyingType, decl.underlyingType));
- APPEND_FMT_LN(o, " if (intVal < %d || intVal > %d) return {};", minVal, maxVal);
- APPEND_FMT_LN(o, " return %s[intVal - %d];", val2StrName, minVal);
- APPEND_LIT_LN(o, "}");
- }
-
- headerOut.AddOutputThing(std::move(lookupFunctionDecl));
- sourceOut.AddOutputThing(std::move(lookupFunctionDef));
- } break;
-
- case EVP_Bits: {
- GenerateEnumStringArray(sourceOut, decl, val2StrName, filteredElements);
- // TODO
- } break;
-
- case EVP_Random: {
- GenerateEnumStringMap(sourceOut, decl, val2StrName, filteredElements);
- // TODO
- } break;
-
- case EVP_COUNT: break;
- }
- }
-
- if (decl.generateFromString) {
- // Generate string -> value lookup table
- INPLACE_FMT(str2ValName, "gCG_%s_Str2Val", mangledName.c_str());
-
- CodegenOutputThing lookupTable;
- {
- auto& o = lookupTable.text;
- // TODO use correct underlying type
- APPEND_FMT_LN(o, "constinit frozen::unordered_map<frozen::string, %s, %" PRId64 "> %s = {", FSTR_LUT_LOOKUP(EnumUnderlyingType, decl.underlyingType), filteredElements.size(), str2ValName);
- for (auto& elm : filteredElements) {
- APPEND_FMT_LN(o, "{\"%s\", %" PRId64 "},", elm.name.c_str(), elm.value);
- }
- APPEND_LIT_LN(o, "};");
- }
-
- // Generate lookup function
- CodegenOutputThing lookupFunctionDecl;
- {
- auto& o = lookupFunctionDecl.text;
- APPEND_LIT_LN(o, "template <>");
- APPEND_FMT_LN(o, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value);", enumName.c_str(), enumName.c_str());
- }
-
- CodegenOutputThing lookupFunctionDef;
- {
- auto& o = lookupFunctionDef.text;
- APPEND_LIT_LN(o, "template <>");
- APPEND_FMT_LN(o, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value) {", enumName.c_str(), enumName.c_str());
- APPEND_FMT_LN(o, " auto iter = %s.find(value);", str2ValName);
- APPEND_FMT_LN(o, " if (iter != %s.end()) {", str2ValName);
- APPEND_FMT_LN(o, " return (%s)iter->second;", enumName.c_str());
- APPEND_LIT_LN(o, " } else {");
- APPEND_LIT_LN(o, " return {};");
- APPEND_LIT_LN(o, " }");
- APPEND_LIT_LN(o, "}");
- }
-
- sourceOut.AddOutputThing(std::move(lookupTable));
- headerOut.AddOutputThing(std::move(lookupFunctionDecl));
- sourceOut.AddOutputThing(std::move(lookupFunctionDef));
- }
-}
-
-void GenerateClassProperty(CodegenOutput& headerOutput, CodegenOutput& sourceOutput) {
- CodegenOutputThing thing;
- APPEND_FMT_LN(thing.text, "TypePropertyInfo gCGtype_%s_%s_Property = {", "TODO", "TODO");
- APPEND_LIT_LN(thing.text, "};");
-
- sourceOutput.AddOutputThing(std::move(thing));
-}
-
-void GenerateClassFunction(CodegenOutput& headerOutput, CodegenOutput& sourceOutput) {
- // TODO
-}
-
-void GenerateForClassMetadata(
- CodegenOutput& headerOutput,
- CodegenOutput& sourceOutput,
- const DeclStruct& decl //
-) {
- auto& mangedName = decl.GetMangledName();
- auto mangedNameCstr = mangedName.c_str();
-
- CodegenOutputThing data;
- // TODO generate type id, this needs global scanning
-
- if (!decl.baseClasses.empty()) {
- // Forward declare the variables (which may appear before this section, after this section, or in another TU)
- for (auto& baseClass : decl.baseClasses) {
- auto baseClassIdName = baseClass->name.c_str();
- APPEND_FMT_LN(data.text, "extern const TypeInfo gCGtype_%s_TypeInfo;", baseClassIdName);
- }
- APPEND_FMT_LN(data.text, "const TypeInfo* const gCGtype_%s_BaseClasses[] = {", mangedNameCstr);
- for (auto& baseClass : decl.baseClasses) {
- auto baseClassIdName = baseClass->name.c_str();
- APPEND_FMT_LN(data.text, "gCGtype_%s_TypeInfo,", baseClassIdName);
- }
- APPEND_LIT_LN(data.text, "};");
- }
-
- if (!decl.memberVariables.empty()) {
- APPEND_FMT_LN(data.text, "const TypePropertyInfo gCGtype_%s_Properties[] = {", mangedNameCstr);
- for (auto& property : decl.memberVariables) {
- APPEND_FMT_LN(data.text, "{.name=\"%s\"sv, .getterName=\"%s\"sv, .setterName=\"%s\"sv},", property.name.c_str(), property.getterName.c_str(), property.setterName.c_str());
- }
- APPEND_LIT_LN(data.text, "};");
- }
-
- if (!decl.memberFunctions.empty()) {
- APPEND_FMT_LN(data.text, "const TypeMethodInfo gCGtype_%s_Methods[] = {", mangedNameCstr);
- for (auto& method : decl.memberFunctions) {
- // TODO
- }
- APPEND_LIT_LN(data.text, "};");
- }
-
- APPEND_FMT_LN(data.text, "const TypeInfo gCGtype_%s_TypeInfo = {", mangedNameCstr);
- APPEND_FMT_LN(data.text, ".name = \"%s\"sv,", mangedNameCstr);
- if (!decl.baseClasses.empty()) APPEND_FMT_LN(data.text, ".parents = gCGtype_%s_BaseClasses,", mangedNameCstr);
- if (!decl.memberVariables.empty()) APPEND_FMT_LN(data.text, ".properties = gCGtype_%s_Properties,", mangedNameCstr);
- if (!decl.memberFunctions.empty()) APPEND_FMT_LN(data.text, ".methods = gCGtype_%s_Methods,", mangedNameCstr);
- APPEND_LIT_LN(data.text, "};");
-
- CodegenOutputThing queryFunc;
- APPEND_FMT(queryFunc.text,
- "template <>\n"
- "const TypeInfo* Metadata::GetTypeInfo<%s>() {\n"
- " return &gCGtype_%s_TypeInfo;\n"
- "}\n",
- decl.fullname->c_str(),
- mangedNameCstr);
-
- sourceOutput.AddOutputThing(std::move(data));
- sourceOutput.AddOutputThing(std::move(queryFunc));
-}
-
-struct NamespaceStackframe {
- // The current namespace that owns the brace level, see example
- DeclNamespace* ns = nullptr;
- // Brace depth `ns` was created at (e.g. [std::details].depth == 0)
- int depth = 0;
-};
-
-struct ParserState {
- // TODO
-};
-
-struct ParserOutput {
- // Example:
- // namespace std::details {
- // /* [stack top].ns = std::details */
- // /* [stack top].depth = std */
- // }
- // namespace foo {
- // /* [stack top].ns = foo */
- // /* [stack top].depth = foo */
- // namespace details {
- // /* [stack top].ns = foo::details */
- // /* [stack top].depth = foo::details */
- // }
- // }
- std::vector<NamespaceStackframe> nsStack;
- // The current effective namespace, see example
- DeclNamespace* currentNamespace = nullptr;
-
- DeclStruct* currentStruct = nullptr;
- DeclEnum* currentEnum = nullptr;
- int currentBraceDepth = 0;
- int currentStructBraceDepth = -1;
- int currentEnumBraceDepth = -1;
-};
-
-void HandleDirectiveEnum(AppState& as, ParserOutput& ps, CodegenLexer& lexer) {
- // Consume the directive
- ++lexer.idx;
-
- if (!ps.currentEnum) {
- printf("[ERROR] BRUSSEL_ENUM must be used within a enum\n");
- return;
- }
-
- auto argList = TryConsumeDirectiveArgumentList(lexer);
- auto& lut = RSTR_LUT(EnumMetaGenOptions);
- for (auto& arg : argList) {
- if (arg.empty()) {
- printf("[ERROR] empty argument is invalid in BRUSSEL_ENUM\n");
- continue;
- }
-
- auto& optionDirective = arg[0]->text;
- auto iter = lut.find(optionDirective);
- if (iter == lut.end()) {
- printf("[ERROR] BRUSSEL_ENUM: invalid option '%s'\n", optionDirective.c_str());
- }
-
- auto option = iter->second;
- switch (option) {
- case EMGO_ToString: ps.currentEnum->generateToString = true; break;
- case EMGO_FromString: ps.currentEnum->generateFromString = true; break;
- case EMGO_ExcludeUseHeuristics: ps.currentEnum->generateExcludeUseHeuristics = true; break;
-
- case EMGO_RemovePrefix: {
- if (argList.size() <= 1) {
- printf("[ERROR] missing argument for RemovePrefix");
- break;
- }
- ps.currentEnum->generateRemovingPrefix = arg[1]->text;
- } break;
- case EMGO_AddPrefix: {
- if (argList.size() <= 1) {
- printf("[ERROR] missing argument for AddPrefix");
- break;
- }
- ps.currentEnum->generatingAddingPrefix = arg[1]->text;
- } break;
-
- case EMGO_COUNT: break;
- }
- }
-
- ps.currentEnum->generating = true;
-}
-
-CodegenLexer LexInputFile(AppState& as, std::string_view source) {
- CodegenLexer result;
- result.InitializeFrom(source);
- return result;
-}
-
-void ParseInputFileAndGenerate(AppState& as, CodegenLexer& /*lexingState*/ ls, std::string_view filenameStem) {
-#if CODEGEN_DEBUG_PRINT
- printf("BEGIN tokens\n");
- for (auto& token : ls.tokens) {
- switch (token.type) {
- case CLEX_intlit: {
- printf(" token %-32s = %ld\n", FSTR_LUT_LOOKUP(ClexNames, token.type), token.lexerIntNumber);
- } break;
-
- case CLEX_floatlit: {
- printf(" token %-32s = %f\n", FSTR_LUT_LOOKUP(ClexNames, token.type), token.lexerRealNumber);
- } break;
-
- default: {
- printf(" token %-32s '%s'\n", FSTR_LUT_LOOKUP(ClexNames, token.type), token.text.c_str());
- } break;
- }
- }
- printf("END tokens\n");
-#endif
-
- auto& sourceFile = as.GetOrCreateSourceFile(filenameStem);
- sourceFile.header = true;
- sourceFile.reprocessing = true;
-
- // TODO move lexedTokens and consumption related functions to ParserState struct
-
- ParserOutput po;
-
- auto& tokens = ls.tokens;
- auto& idx = ls.idx;
- while (ls.idx < ls.tokens.size()) {
- auto& token = ls.Current();
-
- bool incrementTokenIdx = true;
- switch (token.Reamalgamate()) {
- case CLEX_id: {
- CppKeyword keyword;
- {
- auto& map = RSTR_LUT(CppKeyword);
- auto iter = map.find(token.text);
- if (iter != map.end()) {
- keyword = iter->second;
- } else {
- keyword = CKw_COUNT; // Skip keyword section
- }
- }
- switch (keyword) {
- case CKw_Namespace: {
- ++idx;
- incrementTokenIdx = false;
-
- int nestingCount = 0;
- while (true) {
- if (tokens[idx].type != CLEX_id) {
- // TODO better error recovery
- // TODO handle annoymous namespaces
- printf("[ERROR] invalid syntax for namespace\n");
- break;
- }
-
- po.currentNamespace = as.runtimeModel->AddNamespace(DeclNamespace{
- .container = po.currentNamespace,
- .name = tokens[idx].text,
- });
-
- // Consume the identifier token
- ++idx;
-
- if (tokens[idx].type == CLEX_ext_double_colon) {
- // Consume the "::" token
- ++idx;
- } else {
- break;
- }
- }
-
- po.nsStack.push_back(NamespaceStackframe{
- .ns = po.currentNamespace,
- .depth = po.currentBraceDepth,
- });
-
- goto endCaseCLEX_id;
- }
-
- case CKw_Struct:
- case CKw_Class: {
- // Consume the 'class' or 'struct' keyword
- ++idx;
- incrementTokenIdx = false;
-
- // For forward declarations, there are always 2 tokens after `class`: an identifier, and the ';' token
- // Example:
- // class MyClass;
- if (tokens[idx + 0].type == CLEX_id &&
- tokens[idx + 1].text == ";")
- {
- // Skip class forward declarations
- idx += 2;
- break;
- }
-
- auto& idenTok = tokens[idx];
- if (idenTok.type != CLEX_id) {
- printf("[ERROR] invalid syntax for struct or class\n");
- break;
- }
-
- DEBUG_PRINTF("[DEBUG] found struct named %s\n", idenTok.text.c_str());
-
- auto& name = idenTok.text;
- auto fullname = Utils::MakeFullName(name, po.currentNamespace);
- DeclStruct structDecl;
- structDecl.sourceFile = &sourceFile;
- structDecl.container = po.currentNamespace;
- structDecl.name = name;
-
- // Consume the identifier token
- ++idx;
-
- if (ls.TryConsumeSingleCharToken(':')) {
- while (true) {
- // Public, protected, etc.
- TryConsumeAnyKeyword(ls);
-
- auto& idenTok = tokens[idx];
- if (idenTok.type != CLEX_id) {
- printf("[ERROR] invalid syntax for class inheritance list\n");
- goto endCase;
- }
-
- // TODO support namespace qualified names
- auto baseClassFullname = Utils::MakeFullName(idenTok.text, po.currentNamespace);
- auto baseClassDecl = as.runtimeModel->FindStruct(baseClassFullname);
- if (baseClassDecl) {
- // TODO retreive class from database
- // ---- Or just silent create it, and assume the code was valid?
- // We silently ignore a non-existent base class, because they may reside in a file that we didn't scan
- structDecl.baseClasses.push_back(baseClassDecl);
- }
-
- // Consume the identifier token
- ++idx;
-
- if (ls.TryConsumeSingleCharToken('{')) {
- // End of base class list
- --idx; // Give the '{' token back to the main loop
- break;
- } else if (!ls.TryConsumeSingleCharToken(',')) {
- // If the list didn't end, we expect a comma (then followed by more entries)
- printf("[ERROR] invalid syntax for class inheritance list\n");
- goto endCase;
- }
-
- // NOTE: we currently only scan one base class to workaround some code inherits from template classes after their initial base class
- // TODO remove this hack
- break;
- }
- }
-
- {
- // Get a pointer to the decl inside CodegenInput's storage
- auto decl = as.runtimeModel->AddStruct(std::move(fullname), std::move(structDecl));
- po.currentStruct = decl;
- po.currentStructBraceDepth = po.currentBraceDepth;
- }
-
- endCase:
- goto endCaseCLEX_id;
- }
-
- case CKw_Enum: {
- if (po.currentStruct) {
- // TODO parsing enums inside classes is currently broken (1. model database is not modeled for this 2. codegen logic is not modeled)
- break;
- }
-
- // Consume the "enum" keyword
- ++idx;
- incrementTokenIdx = false;
-
- StbLexerToken* idenTok;
- if (tokens[idx].text == "class") {
- // Consume the "class" keyword
- ++idx;
- idenTok = &tokens[idx];
- DEBUG_PRINTF("[DEBUG] found enum class named %s\n", idenTok->text.c_str());
- } else {
- idenTok = &tokens[idx];
- DEBUG_PRINTF("[DEBUG] found enum named %s\n", idenTok->text.c_str());
- }
-
- auto& name = tokens[idx].text;
- auto fullname = Utils::MakeFullName(name, po.currentNamespace);
- DeclEnum enumDecl;
- enumDecl.sourceFile = &sourceFile;
- enumDecl.container = po.currentNamespace;
- enumDecl.name = name;
- // Setting underlying type: see below
-
- // Temporarily bind the pointers to local variable, HandleDirectiveEnum() and other functions expect these to the set
- po.currentEnum = &enumDecl;
- po.currentEnumBraceDepth = po.currentBraceDepth;
-
- // Consume the enum name identifier
- ++idx;
-
- // Setting underlying type
- if (auto eut = TryConsumeEnumUnderlyingTypeClause(ls);
- eut != EUT_COUNT)
- {
- enumDecl.underlyingType = eut;
- } else {
- enumDecl.underlyingType = EUT_Int32;
- }
-
- int enumClosingBraceCount = 0;
- int enumBraceDepth = 0;
- while (enumClosingBraceCount == 0 && idx < tokens.size()) {
- auto& token = tokens[idx];
- switch (token.Reamalgamate()) {
- case CLEX_id: {
- if (token.text == "BRUSSEL_ENUM") {
- // Consume the argument list and skip advancing index: this function already consumed all the tokens about BRUSSEL_ENUM
- HandleDirectiveEnum(as, po, ls);
- continue;
- } else {
- auto& vec = enumDecl.elements;
- // Set to the previous enum element's value + 1, or starting from 0 if this is the first
- // Also overridden in the CLEX_intlit branch
- auto value = vec.empty() ? 0 : vec.back().value + 1;
- vec.push_back(DeclEnumElement{
- .name = token.text,
- .value = value,
- });
- }
- } break;
-
- case CLEX_intlit: {
- auto& vec = enumDecl.elements;
- if (!vec.empty()) {
- auto& lastElm = vec.back();
- lastElm.value = token.lexerIntNumber;
- }
- } break;
-
- case '{': {
- ++enumBraceDepth;
- } break;
-
- case '}': {
- --enumBraceDepth;
- ++enumClosingBraceCount;
- } break;
- }
-
- ++idx;
- }
-
- {
- auto decl = as.runtimeModel->AddEnum(std::move(fullname), std::move(enumDecl));
- // Fix pointers
- po.currentEnum = decl;
- po.currentEnumBraceDepth = po.currentBraceDepth;
- }
-
- if (po.currentEnum->generating) {
- as.enumsToRevisit.push_back(po.currentEnum);
- }
-
- // NOTE: we parse the whole enum at once (above code), the enum ends right here after the closing brace '}'
- po.currentEnum = nullptr;
- po.currentEnumBraceDepth = -1;
-
- goto endCaseCLEX_id;
- }
-
- // Consume the whole statement, because this statement may contain `enum` or `class` keywords that will pollute the parser
- case CKw_Template: {
- // `template` is either a template list which we don't care about, or a part of a type which we don't care about,
- // unless it's a part of a function declaration, where the tokens are handled inside CG_ClassMethod parsing
- // TODO handle nested templates or operator> inside template expression
- ls.SkipUntilTokenSingleChar('>');
- } break;
- case CKw_Using: {
- // `using` indicates a type alias or namespace import which we don't care about
- ls.SkipUntilTokenSingleChar(';');
- } break;
-
- // We don't care about these keywords
- case CKw_Public:
- case CKw_Protected:
- case CKw_Private:
- case CKw_Virtual:
- case CKw_COUNT: break;
- }
-
- CodegenDirective directive;
- {
- auto& map = RSTR_LUT(CodegenDirective);
- auto iter = map.find(token.text);
- if (iter != map.end()) {
- directive = iter->second;
- } else {
- directive = CD_COUNT; // Skip directive section
- }
- }
- switch (directive) {
- case CD_Class: {
- // Consume the directive
- ++idx;
- incrementTokenIdx = false;
-
- if (!po.currentStruct) {
- printf("[ERROR] BRUSSEL_CLASS must be used within a class or struct\n");
- break;
- }
-
- // Always-on option
- po.currentStruct->generating = true;
-
- auto argList = TryConsumeDirectiveArgumentList(ls);
- auto& lut = RSTR_LUT(StructMetaGenOptions);
- for (auto& arg : argList) {
- if (arg.empty()) {
- printf("[ERROR] empty argument is invalid in BRUSSEL_CLASS\n");
- continue;
- }
-
- auto& optionDirective = arg[0]->text;
- auto iter = lut.find(optionDirective);
- if (iter == lut.end()) continue;
- switch (iter->second) {
- case SMGO_InheritanceHiearchy: po.currentStruct->generatingInheritanceHiearchy = true; break;
- case SMGO_COUNT: break;
- }
- }
-
- goto endCaseCLEX_id;
- }
-
- case CD_ClassProperty: {
- // Consume the directive
- ++idx;
- incrementTokenIdx = false;
-
- if (!po.currentStruct ||
- !po.currentStruct->generating)
- {
- printf("[ERROR] BRUSSEL_PROPERTY must be used within a class or struct, that has the BRUSSEL_CLASS directive\n");
- break;
- }
-
- auto argList = TryConsumeDirectiveArgumentList(ls);
- auto declOpt = TryConsumeMemberVariable(ls);
- if (!declOpt.has_value()) {
- printf("[ERROR] a member variable must immediately follow a BRUSSEL_PROPERTY\n");
- break;
- }
- auto& decl = declOpt.value();
- decl.containerStruct = po.currentStruct;
-
- // Different option's common logic
- std::string pascalCaseName;
- auto GetPascalCasedName = [&]() -> const std::string& {
- if (pascalCaseName.empty()) {
- pascalCaseName = Utils::MakePascalCase(decl.name);
- }
- return pascalCaseName;
- };
-
- auto& lut = RSTR_LUT(StructPropertyOptions);
- for (auto& arg : argList) {
- if (arg.empty()) {
- printf("[ERROR] empty argument is invalid in BRUSSEL_PROPERTY\n");
- continue;
- }
-
- auto& optionDirective = arg[0]->text;
- auto iter = lut.find(optionDirective);
- if (iter == lut.end()) continue;
- switch (iter->second) {
- case SPO_Getter: {
- // NOTE: I'm too lazy to write error checks, just let the codegen crash if syntax is invalid
- auto& getterName = arg.at(1)->text;
- if (getterName == "auto") {
- // NOTE: intentionally shadowing
- INPLACE_FMT(getterName, "Get%s", GetPascalCasedName().c_str());
-
- decl.getterName = getterName;
- decl.isGetterGenerated = true;
- } else {
- decl.getterName = getterName;
- }
- } break;
-
- case SPO_Setter: {
- // NOTE: I'm too lazy to write error checks, just let the codegen crash if syntax is invalid
- auto& setterName = arg.at(1)->text;
- if (setterName == "auto") {
- // NOTE: intentionally shadowing
- INPLACE_FMT(setterName, "Set%s", GetPascalCasedName().c_str());
-
- decl.setterName = setterName;
- decl.isSetterGenerated = true;
- } else {
- decl.setterName = setterName;
- }
- } break;
-
- case SPO_COUNT: break;
- }
- }
-
- po.currentStruct->memberVariables.push_back(std::move(decl));
-
- goto endCaseCLEX_id;
- }
-
- case CD_ClassMethod: {
- // Consume the directive
- ++idx;
- incrementTokenIdx = false;
-
- goto endCaseCLEX_id;
- }
-
- case CD_XGlobalVar: {
- // TODO
- goto endCaseCLEX_id;
- }
-
- case CD_XGlobalVarCtor: {
- // TODO
- goto endCaseCLEX_id;
- }
-
- case CD_XGlobalVarDtor: {
- // TODO
- goto endCaseCLEX_id;
- }
-
- // This directive always appear inside a enum{} block, which is handled above in the keywords section
- case CD_Enum:
- case CD_COUNT: break;
- }
-
- endCaseCLEX_id:;
- } break;
-
- case '{': {
- po.currentBraceDepth++;
- if (po.currentBraceDepth < 0) {
- printf("[WARNING] unbalanced brace\n");
- }
- } break;
-
- case '}': {
- po.currentBraceDepth--;
- if (po.currentBraceDepth < 0) {
- printf("[WARNING] unbalanced brace\n");
- }
-
- if (!po.nsStack.empty()) {
- auto& ns = po.nsStack.back();
- if (ns.depth == po.currentBraceDepth) {
- po.nsStack.pop_back();
-
- if (!po.nsStack.empty()) {
- po.currentNamespace = po.nsStack.back().ns;
- } else {
- po.currentNamespace = nullptr;
- }
- }
- }
-
- if (po.currentStruct && po.currentBraceDepth == po.currentStructBraceDepth) {
- // Exit struct
-
- if (po.currentStruct->generating) {
- as.structsToRevisit.push_back(po.currentStruct);
- }
-
- po.currentStruct = nullptr;
- po.currentStructBraceDepth = -1;
- }
- if (po.currentEnum && po.currentBraceDepth == po.currentEnumBraceDepth) {
- // Exit enum
-
- // TODO this is unused currently, see CKw_Enum branch
- if (po.currentEnum->generating) {
- as.enumsToRevisit.push_back(po.currentEnum);
- }
-
- po.currentEnum = nullptr;
- po.currentEnumBraceDepth = -1;
- }
- } break;
- }
-
- if (incrementTokenIdx) {
- ++idx;
- }
- }
-
- if (po.currentBraceDepth != 0) {
- printf("[WARNING] unbalanced brace at end of file\n");
- }
-
- as.archiveModel->DeleteDeclsRelatedToFile(filenameStem);
- // as.modelArchive->Store(po.model);
-}
-
-void HandleInputFile(AppState& as, const fs::path& path) {
- auto filenameStem = path.stem().string();
- auto lexingState = LexInputFile(as, Utils::ReadFileAsString(path));
- ParseInputFileAndGenerate(as, lexingState, filenameStem);
-}
-
-enum InputOpcode {
- IOP_ProcessSingleFile,
- IOP_ProcessRecursively,
- IOP_ProcessFileList,
- IOP_COUNT,
-};
-
-void HandleArgument(AppState& as, InputOpcode opcode, std::string_view operand) {
- switch (opcode) {
- case IOP_ProcessSingleFile: {
- DEBUG_PRINTF("Processing single file %.*s\n", PRINTF_STRING_VIEW(operand));
- HandleInputFile(as, fs::path(operand));
- } break;
-
- case IOP_ProcessRecursively: {
- DEBUG_PRINTF("Recursively processing folder %.*s\n", PRINTF_STRING_VIEW(operand));
-
- fs::path startPath(operand);
- for (auto& item : fs::recursive_directory_iterator(startPath)) {
- if (!item.is_regular_file()) {
- continue;
- }
-
- auto& path = item.path();
- if (auto pathExt = path.extension();
- pathExt != ".h" &&
- pathExt != ".hpp")
- {
- continue;
- }
-
- DEBUG_PRINTF("Processing subfile %s\n", path.string().c_str());
- HandleInputFile(as, path);
- }
- } break;
-
- case IOP_ProcessFileList: {
- DEBUG_PRINTF("Processing file list %.*s\n", PRINTF_STRING_VIEW(operand));
-
- fs::path fileListPath(operand);
- auto fileList = Utils::OpenCstdioFile(fileListPath, Utils::Read);
- if (!fileList) {
- // NOTE: need this because our dirty-file-list generation algorithm in CMakeLists.txt doesn't produce a file when nothing is changed
- DEBUG_PRINTF("File-list file does not exist, silently skipping.\n");
- break;
- }
- DEFER {
- fclose(fileList);
- };
-
- std::string line;
- while (Utils::ReadCstdioLine(fileList, line)) {
- // Remove '\n'
- line.pop_back();
-
- DEBUG_PRINTF("Processing file in list %.*s\n", line.c_str());
- HandleInputFile(as, fs::path(line));
- }
- } break;
-
- case IOP_COUNT: break;
- }
-}
-
-InputOpcode ParseInputOpcode(std::string_view text) {
- if (text == "single"sv) {
- return IOP_ProcessSingleFile;
- } else if (text == "rec"sv) {
- return IOP_ProcessRecursively;
- } else if (text == "fileList"sv) {
- return IOP_ProcessFileList;
- } else {
- INPLACE_FMT(msg, "Unknown input opcode %s\n", text.data());
- throw std::runtime_error(msg);
- }
-}
-
-int main(int argc, char* argv[]) {
- FSTR_LUT_INIT(ClexNames);
- FSTR_LUT_INIT(EnumUnderlyingType);
- RSTR_LUT_INIT(EnumUnderlyingType);
- FSTR_LUT_INIT(EnumValuePattern);
- RSTR_LUT_INIT(CppKeyword);
- RSTR_LUT_INIT(CodegenDirective);
- RSTR_LUT_INIT(StructMetaGenOptions);
- RSTR_LUT_INIT(StructPropertyOptions);
- RSTR_LUT_INIT(EnumMetaGenOptions);
-
- // TODO better arg parser
- // option 1: use cxxopts and positional arguments
- // option 2: take one argument only, being a json objecet
-
- AppState as;
-
- // If no cli is provided (argv[0] conventionally but not mandatorily the cli), this will do thing
- // Otherwise, start with the 2nd element in the array, which is the 1st actual argument
- if (argc <= 1) {
- // NOTE: keep in sync with various enum options and parser code
- printf(&R"""(
-USAGE: codegen.exe --output-dir=<path> [--database=<path>] [<opcode>:<input path>]...
-where --output-dir=<path>: the *directory* to write generated contents to. This will NOT automatically create the directory.
- --database=<path>: the *file* to use for the code model database.
- <opcode> is one of:
- "single" process this <input path> file only
- "rec" starting at the given directory <input path>, recursively process all .h .hpp files
- "fileList" read <input path> as a text file, and process each line as a separate file path
-)"""[1]);
- return -1;
- }
-
- // Named argument pass
- robin_hood::unordered_map<std::string_view, std::string_view*> namedArguments{
- { "output-dir"sv, &as.outputDir },
- { "database"sv, &as.databaseFilePath },
- };
- for (int i = 1; i < argc; ++i) {
- std::string_view arg(argv[i]);
- if (!arg.starts_with("--")) {
- // Convention: a "--" argument indicates everything afterwords are positional arguments
- if (arg.size() == 2) {
- break;
- } else {
- continue;
- }
- }
-
- size_t equalLoc = arg.find('=');
- auto oper = arg.substr(/*--*/ 2, equalLoc - 2);
- auto iter = namedArguments.find(oper);
- if (iter != namedArguments.end()) {
- auto storage = iter->second;
- if (storage) {
- if (equalLoc == std::string_view::npos) {
- *storage = ""sv;
- } else {
- *storage = arg.substr(equalLoc + 1);
- }
- }
- }
- }
-
- DEBUG_PRINTF("Outputting to directory %.*s.\n", PRINTF_STRING_VIEW(as.outputDir));
- DEBUG_PRINTF("Databse file: %.*s.\n", PRINTF_STRING_VIEW(as.databaseFilePath));
-
- // TODO move the actual output logic after processing all input commands, based on SQLite batabase model instead of the in-memory CodegenModel model
- // this allows better consistency between direct in-file entities (like enums) vs. multi-file entities (like struct inheritance hierarchy)
- // this would also mean almost rewriting the whole codegen logic, to work on a changelist fetched from SQLite database instead of being embedded inside the parser loop
- // TODO how do we detect the case of
- // 1. has: Foo.hpp Bar.hpp
- // 2. struct Foo; struct Bar : Foo;
- // 3. struct Foo is removed from Foo.hpp, but our parser only recieves Foo.hpp as file changed--and can't figure out that there is still a reference to Foo in Bar.hpp
- // possible solutions
- // - use some kind of database scanner to review all references to a class when removing (e.g. detect for logic error on foreign key linked columns)
- // - follow the file links in database, and propagate parsing to those files in the hierarchy
- // - pretty much defeats the purpose of using an incremental parser: some classes like GameObject will have links throughout a very large portion of the project code
- // - [x] out of parser generation
- // - [ ] database readback
- // - [ ] full database based generation (tentative)
- CodegenRuntimeModel runtimeModel;
- CodegenArchiveModel archiveModel(as.databaseFilePath);
-
- as.runtimeModel = &runtimeModel;
- as.archiveModel = &archiveModel;
-
- // Positional argument pass
- for (int i = 1; i < argc; ++i) {
- std::string_view arg(argv[i]);
- if (arg.starts_with("--")) {
- continue;
- }
-
- DEBUG_PRINTF("Processing input command %s\n", argv[i]);
-
- auto separatorLoc = arg.find(':');
- if (separatorLoc != std::string_view::npos) {
- auto opcodeString = arg.substr(0, separatorLoc);
- auto opcode = ParseInputOpcode(opcodeString);
- auto operand = arg.substr(separatorLoc + 1);
-
- HandleArgument(as, opcode, operand);
- }
- }
-
- for (auto decl : as.enumsToRevisit) {
- if (!decl->generating) {
- continue;
- }
-
- auto& headerOutput = decl->sourceFile->postHeaderOutput;
- auto& sourceOutput = decl->sourceFile->postSourceOutput;
- GenerateForEnum(headerOutput, sourceOutput, *decl);
- }
- for (auto decl : as.structsToRevisit) {
- if (!decl->generating) {
- continue;
- }
-
- auto& headerOutput = decl->sourceFile->postHeaderOutput;
- auto& sourceOutput = decl->sourceFile->postSourceOutput;
-
- // Always-on metdata
- GenerateForClassMetadata(headerOutput, sourceOutput, *decl);
-
- if (decl->generatingInheritanceHiearchy) {
- // TODO
- }
-
- for (auto& property : decl->memberVariables) {
- if (property.isGetterGenerated) {
- // TODO work with pass-by-value vs pass-by-reference
- // this probably needs libclang to detect the size and existance of trivial copy-ctors
- CodegenOutputThing data;
- APPEND_FMT_LN(data.text, "const %s& %s::%s() const {", property.type.c_str(), property.containerStruct->fullname->c_str(), property.getterName.c_str());
- APPEND_FMT_LN(data.text, " return %s;", property.name.c_str());
- APPEND_LIT_LN(data.text, "}");
-
- sourceOutput.AddOutputThing(std::move(data));
- }
- if (property.isSetterGenerated) {
- CodegenOutputThing data;
- APPEND_FMT_LN(data.text, "void %s::%s(const %s& value) const {", property.containerStruct->fullname->c_str(), property.setterName.c_str(), property.type.c_str());
- APPEND_FMT_LN(data.text, " this->%s = value;", property.name.c_str());
- APPEND_LIT_LN(data.text, "}");
-
- sourceOutput.AddOutputThing(std::move(data));
- }
- }
- for (auto& method : decl->memberFunctions) {
- // TODO
- }
- }
-
- archiveModel.Store(runtimeModel);
-
- // Write output files
- for (auto&& [_, sourceFile] : as.sourceFiles) {
- INPLACE_FMT(hpp, "%.*s.gh.inl", PRINTF_STRING_VIEW(sourceFile.filename));
- INPLACE_FMT(cpp, "%.*s.gs.inl", PRINTF_STRING_VIEW(sourceFile.filename));
- Utils::ProduceGeneratedHeader(hpp, sourceFile.postHeaderOutput, cpp, sourceFile.postSourceOutput);
-
- INPLACE_FMT(generatedHeaderInlName, "%.*s/%s", PRINTF_STRING_VIEW(as.outputDir), hpp);
- Utils::WriteOutputFile(sourceFile.postHeaderOutput, generatedHeaderInlName);
- INPLACE_FMT(generatedSourceInlName, "%.*s/%s", PRINTF_STRING_VIEW(as.outputDir), cpp);
- Utils::WriteOutputFile(sourceFile.postSourceOutput, generatedSourceInlName);
- INPLACE_FMT(generatedCppName, "%.*s/%.*s.g.cpp", PRINTF_STRING_VIEW(as.outputDir), PRINTF_STRING_VIEW(sourceFile.filename));
- Utils::WriteOutputFile(sourceFile.tuOutput, generatedCppName);
- }
-
- return 0;
-}
-
-// TODO move this function to CodegenDecl.cpp, after making LUT able to cross TUs
-std::string_view DeclEnum::GetUnderlyingTypeName() const {
- return FSTR_LUT_LOOKUP(EnumUnderlyingType, underlyingType);
-}
diff --git a/source/20-codegen-compiler/test/examples/TestClass.hpp.txt b/source/20-codegen-compiler/test/examples/TestClass.hpp.txt
deleted file mode 100644
index 3eed8db..0000000
--- a/source/20-codegen-compiler/test/examples/TestClass.hpp.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-#include <TestClass.gph.inl>
-
-class MyClass {
- BRUSSEL_CLASS()
-
-public:
- BRUSSEL_PROPERTY(GETTER GetName, SETTER SetName)
- std::string name;
-
- BRUSSEL_PROPERTY(GETTER auto, SETTER auto)
- std::string tag;
-
- BRUSSEL_PROPERTY()
- int foo;
-
- BRUSSEL_PROPERTY()
- int bar;
-
-public:
- const std::string& GetName() const { return name; }
- void SetName(std::string name) { this->name = std::move(name); }
-};
-
-namespace MyNamespace {
-struct Base {
- BRUSSEL_CLASS(InheritanceHiearchy)
-};
-
-struct DerviedFoo : public Base {
- BRUSSEL_CLASS()
-};
-
-struct DerviedBar : Base {
- BRUSSEL_CLASS()
-};
-}
-
-#include <TestClass.gh.inl>
diff --git a/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt b/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt
deleted file mode 100644
index 132bac0..0000000
--- a/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-enum MyEnum {
- BRUSSEL_ENUM(ToString, FromString)
- EnumElement1,
- EnumElement2,
- EnumElement3,
-};
-
-// Let's also test enum class
-enum class CountedEnumAll {
- BRUSSEL_ENUM(ToString, FromString)
- CEA_Foo,
- CEA_Bar,
- CEA_COUNT,
-};
-
-enum CountedEnum : unsigned short int {
- BRUSSEL_ENUM(ToString, FromString, RemovePrefix CE_, AddPrefix CustomPrefix, ExcludeHeuristics)
- CE_Foo,
- CE_Bar,
- CE_FooBar,
- CE_COUNT,
-};
-
-namespace MyNamespace {
- enum class MyNamespacedEnum {
- BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics)
- MNE_Foo,
- MNE_Bar,
- };
-
- namespace details {
- enum MyNamespacedEnum {
- BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics)
- MNE_Foo,
- MNE_Bar,
- };
- }
-}
-
-namespace foo::details {
- enum Enum {
- BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics)
- };
-}