diff options
Diffstat (limited to 'source/20-codegen-compiler')
-rw-r--r-- | source/20-codegen-compiler/CodegenModel.cpp | 177 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenModelArchive.hpp | 8 | ||||
-rw-r--r-- | source/20-codegen-compiler/main.cpp | 27 |
3 files changed, 194 insertions, 18 deletions
diff --git a/source/20-codegen-compiler/CodegenModel.cpp b/source/20-codegen-compiler/CodegenModel.cpp index 0017b62..88439e8 100644 --- a/source/20-codegen-compiler/CodegenModel.cpp +++ b/source/20-codegen-compiler/CodegenModel.cpp @@ -16,6 +16,8 @@ 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; }; @@ -55,6 +57,10 @@ struct SQLiteStatement { 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 @@ -98,9 +104,10 @@ public: SQLiteStatement beginTransactionStmt; SQLiteStatement commitTransactionStmt; SQLiteStatement rollbackTransactionStmt; - SQLiteStatement storeFileStmt; SQLiteStatement findFileStmt; + SQLiteStatement storeFileStmt; SQLiteStatement findNamespaceStmt; + SQLiteStatement getNamespaceStmt; SQLiteStatement storeNamespaceStmt; /* Component Statements, initalized on demand */ SQLiteStatement storeStructStmt; @@ -112,6 +119,8 @@ public: SQLiteStatement deleteFunctionDeclByFilenameStmt; SQLiteStatement deleteStructDeclByFilenameStmt; SQLiteStatement deleteEnumDeclByFilenameStmt; + // TODO + // SQLiteStatement getRootClassStmt; void InitializeDatabase() { char* errMsg = nullptr; @@ -120,7 +129,6 @@ public: PrintErrMsgIfPresent(errMsg); assert(result == SQLITE_OK); - // TODO create a table of file names // TODO unique with overloading, and container structs result = sqlite3_exec(database, R"""( BEGIN TRANSACTION; @@ -140,7 +148,7 @@ CREATE TABLE Namespaces( CREATE TABLE DeclFunctions( Id INTEGER PRIMARY KEY, - FileId INTEGER REFERENCES Files(Id), + FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE, NamespaceId INTEGER REFERENCES Namespaces(Id), Name TEXT ); @@ -153,7 +161,7 @@ CREATE TABLE DeclFunctionParameters( CREATE TABLE DeclStructs( Id INTEGER PRIMARY KEY, - FileId INTEGER REFERENCES Files(Id), + FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE, NamespaceId INTEGER REFERENCES Namespaces(Id), Name TEXT, IsMetadataMarked INTEGER @@ -193,7 +201,7 @@ CREATE TABLE DeclStructMethodParameters( CREATE TABLE DeclEnums( Id INTEGER PRIMARY KEY, - FileId INTEGER REFERENCES Files(Id), +FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE, NamespaceId INTEGER REFERENCES Namespaces(Id), Name TEXT, UnderlyingType TEXT @@ -286,6 +294,55 @@ COMMIT TRANSACTION; return nsId; } + // TODO maybe merge with Utils::MakeFullName? + std::string GetNamespaceFullName(int64_t nsId) { + std::vector<std::string> namespaceNames; + size_t fullnameLength = 0; + + sqlite3_stmt* stmt = getNamespaceStmt; + int64_t currentNsId = nsId; + while (true) { + sqlite3_bind_int64(stmt, 1, currentNsId); + + int result = sqlite3_step(stmt); + DEFER { + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + }; + assert(result == SQLITE_ROW); + + /* 0th column Id */ + int64_t parentNamespaceId = sqlite3_column_int64(stmt, 1); + auto nameCstr = (const char*)sqlite3_column_text(stmt, 2); + + currentNsId = parentNamespaceId; + std::string name(nameCstr); + fullnameLength += name.size() + 2; + namespaceNames.push_back(std::move(name)); + + if (parentNamespaceId == kGlobalNamespaceId) { + break; + } + } + fullnameLength -= 2; + + std::string fullname; + fullname.reserve(fullnameLength); + + for (auto it = namespaceNames.rbegin(); it != namespaceNames.rend(); ++it) { + fullname.append(*it); + if (std::next(it) != namespaceNames.rend()) { + fullname.append("::"); + } + } + + return fullname; + } + + std::string GetDeclFullName(int64_t nsId, std::string_view declName) { + return GetNamespaceFullName(nsId).append(declName); + } + /// \return Row ID of the file, or 0 if it currently doesn't exist. int64_t FindFile(std::string_view filename) { sqlite3_stmt* stmt = findFileStmt; @@ -498,6 +555,7 @@ CodegenModelArchive::CodegenModelArchive(std::string_view dbPath) 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"); } @@ -528,8 +586,8 @@ void CodegenModelArchive::DeleteDeclsRelatedToFile(std::string_view filename) { m->CommitTransaction(); } -void CodegenModelArchive::Store(const CodegenModel& cgInput) { - auto& cgm = cgInput.GetPimpl(); +void CodegenModelArchive::Store(const CodegenModel& cgModel) { + auto& cgm = cgModel.GetPimpl(); struct Visiter { CodegenModelArchive* self; @@ -551,6 +609,7 @@ void CodegenModelArchive::Store(const CodegenModel& cgInput) { // TODO why are some namepsaces not found in StoreStruct or StoreEnum calls, if we don't use FindOrStoreNamespace there? // since we store all currently scanned namespaces into the model here, does that mean the parser is not correctly picking some of those up? // but if the parser is failing, than the DeclThing objects should have a null `container` field? + // [2022-07-15] I think this bug is fixed, it was caused by FindOrStoreNamespace() not calling sqlite3_reset(), causing a stmt execution state mixup between different namespaces 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); @@ -562,6 +621,110 @@ void CodegenModelArchive::Store(const CodegenModel& cgInput) { m->CommitTransaction(); } +void CodegenModelArchive::LoadInto(CodegenModel& model) const { + // TODO +} + +CodegenModel CodegenModelArchive::Load() const { + CodegenModel 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; + + // NOTE: this is basically { initializer; while(true) { body; } } in a short form + // Load structs + for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructs"sv);;) { + int result = sqlite3_step(stmt); + if (result == SQLITE_DONE) { + break; + } + + if (result == SQLITE_ROW) { + int64_t id = sqlite3_column_int64(stmt, 9); + int64_t fileId = sqlite3_column_int64(stmt, 1); + int64_t nsId = sqlite3_column_int64(stmt, 2); + std::string_view name((const char*)sqlite3_column_text(stmt, 3)); + + auto decl = cgModel.AddStruct(m->GetDeclFullName(nsId, name), DeclStruct{}); + structsById.try_emplace(id, decl); + + continue; + } + + // Executing the statement resulted in an error + assert(false); + } + // Load struct's base classes + for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructBaseClassRelations");;) { + int result = sqlite3_step(stmt); + if (result == SQLITE_DONE) { + break; + } + + if (result == SQLITE_ROW) { + int64_t structId = sqlite3_column_int64(stmt, 0); + int64_t parentStructNsId = sqlite3_column_int64(stmt, 1); + std::string_view parentStructName((const char*)sqlite3_column_text(stmt, 2)); + + auto declThis = structsById.at(structId); + auto declParent = cgModel.FindStruct(parentStructName); // TODO namespace + declThis->baseClasses.push_back(declParent); + + continue; + } + + assert(false); + } + // Load struct properties + for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructProperties"sv);;) { + int result = sqlite3_step(stmt); + if (result == SQLITE_DONE) { + break; + } + + if (result == SQLITE_ROW) { + // TODO + continue; + } + + assert(false); + } + // Load struct methods + for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethods"sv);;) { + int result = sqlite3_step(stmt); + if (result == SQLITE_DONE) { + break; + } + + if (result == SQLITE_ROW) { + // TODO + continue; + } + + assert(false); + } + // Load method params + for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethodParameters"sv);;) { + int result = sqlite3_step(stmt); + if (result == SQLITE_DONE) { + break; + } + + if (result == SQLITE_ROW) { + // TODO + continue; + } + + assert(false); + } + + return cgModel; +} + void CodegenModelArchive::StoreStruct(const DeclStruct& decl) { // -Argument- -Description- // ?1 Namespace ID diff --git a/source/20-codegen-compiler/CodegenModelArchive.hpp b/source/20-codegen-compiler/CodegenModelArchive.hpp index f548048..e67f4d9 100644 --- a/source/20-codegen-compiler/CodegenModelArchive.hpp +++ b/source/20-codegen-compiler/CodegenModelArchive.hpp @@ -4,8 +4,8 @@ #include "CodegenModel.hpp" #include <sqlite3.h> -#include <string_view> #include <cstdint> +#include <string_view> class CodegenModelArchive { private: @@ -20,7 +20,11 @@ public: Private& GetPimpl() const { return *m; } void DeleteDeclsRelatedToFile(std::string_view filename); - void Store(const CodegenModel& cgInput); + + void Store(const CodegenModel& model); + void LoadInto(CodegenModel& model) const; + CodegenModel Load() const; + void StoreStruct(const DeclStruct& decl); void StoreFunction(const DeclFunction& decl); void StoreEnum(const DeclEnum& decl); diff --git a/source/20-codegen-compiler/main.cpp b/source/20-codegen-compiler/main.cpp index dcec0d3..81b5d23 100644 --- a/source/20-codegen-compiler/main.cpp +++ b/source/20-codegen-compiler/main.cpp @@ -30,6 +30,7 @@ namespace fs = std::filesystem; // TOOD maybe switch to libclang, maintaining this parser is just too painful struct AppState { + CodegenModel* model; CodegenModelArchive* modelArchive; robin_hood::unordered_map<std::string, SourceFile, StringHash, StringEqual> sourceFiles; std::string_view outputDir; @@ -1192,10 +1193,8 @@ void ParseInputFileAndGenerate(AppState& as, CodegenLexer& /*lexingState*/ ls, s INPLACE_FMT(generatedCppName, "%.*s/%.*s.g.cpp", PRINTF_STRING_VIEW(as.outputDir), PRINTF_STRING_VIEW(filenameStem)); Utils::WriteOutputFile(po.standaloneSourceOutput, generatedCppName); - if (as.modelArchive) { - as.modelArchive->DeleteDeclsRelatedToFile(filenameStem); - as.modelArchive->Store(po.model); - } + as.modelArchive->DeleteDeclsRelatedToFile(filenameStem); + as.modelArchive->Store(po.model); } void HandleInputFile(AppState& as, const fs::path& path) { @@ -1348,13 +1347,23 @@ where --output-dir=<path>: the *directory* to write generated contents to. Thi DEBUG_PRINTF("Outputting to directory %.*s.\n", PRINTF_STRING_VIEW(as.outputDir)); DEBUG_PRINTF("Databse file: %.*s.\n", PRINTF_STRING_VIEW(as.databaseFilePath)); -#define BRUSSEL_ENABLE_CODEGEN_ARCHIVE 1 -#if BRUSSEL_ENABLE_CODEGEN_ARCHIVE + // TODO make every input command share the same CodegenModel + // 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 + CodegenModel model; CodegenModelArchive archive(as.databaseFilePath); + + as.model = &model; as.modelArchive = &archive; -#else - as.modelArchive = nullptr; -#endif // Positional argument pass for (int i = 1; i < argc; ++i) { |