diff options
author | rtk0c <[email protected]> | 2023-09-20 23:58:58 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2023-09-20 23:58:58 -0700 |
commit | f138311d2d2e0cc9ba0496d523bb46f2c1c9fb73 (patch) | |
tree | f96100a813a4ffb28dcd074455d3a2f8ee426430 /source/20-codegen-compiler/CodegenModel.cpp |
Copy from the PlasticSCM repo, replace vendored glm wtih conan
Diffstat (limited to 'source/20-codegen-compiler/CodegenModel.cpp')
-rw-r--r-- | source/20-codegen-compiler/CodegenModel.cpp | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/source/20-codegen-compiler/CodegenModel.cpp b/source/20-codegen-compiler/CodegenModel.cpp new file mode 100644 index 0000000..303ad4e --- /dev/null +++ b/source/20-codegen-compiler/CodegenModel.cpp @@ -0,0 +1,732 @@ +#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(); + } +} |