From 91cce4c1703ee5e40be90f303b86ab3317e5b762 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Sat, 18 Feb 2023 21:06:42 -0300 Subject: [PATCH] refactor and complete parts of the knowledge base --- src/{ => lib}/knowledge_base.cpp | 32 ++++- src/{ => lib}/knowledge_base.h | 29 ++++- src/lib/meson.build | 3 +- src/lib/models/action.cpp | 126 ++++++++++++++++++ src/lib/models/action.h | 22 +++- src/lib/models/entity.h | 9 +- src/lib/models/error.h | 89 +++++++++++++ src/lib/models/fact.cpp | 2 +- src/lib/models/fact.h | 137 ++++++++++++++++++- src/lib/models/rule.h | 78 +++++++++++ src/lib/models/suggest_action.cpp | 210 ++++++++++++++++++++++++++++++ src/lib/models/suggest_action.h | 112 ++++++++++++++++ src/lib/models/verb.h | 2 +- src/lib/obelisk.cpp | 2 - src/meson.build | 3 +- src/parser.cpp | 23 +++- src/parser.h | 1 + 17 files changed, 851 insertions(+), 29 deletions(-) rename src/{ => lib}/knowledge_base.cpp (86%) rename src/{ => lib}/knowledge_base.h (85%) diff --git a/src/knowledge_base.cpp b/src/lib/knowledge_base.cpp similarity index 86% rename from src/knowledge_base.cpp rename to src/lib/knowledge_base.cpp index 4227231..e0b48f6 100644 --- a/src/knowledge_base.cpp +++ b/src/lib/knowledge_base.cpp @@ -1,11 +1,5 @@ #include "knowledge_base.h" -#include "models/action.h" -#include "models/entity.h" #include "models/error.h" -#include "models/fact.h" -#include "models/rule.h" -#include "models/suggest_action.h" -#include "models/verb.h" #include #include @@ -119,6 +113,25 @@ void obelisk::KnowledgeBase::addVerbs(std::vector& verbs) } } +void obelisk::KnowledgeBase::addActions(std::vector& actions) +{ + for (auto& action : actions) + { + try + { + action.insert(dbConnection_); + } + catch (obelisk::DatabaseConstraintException& exception) + { + // ignore unique constraint error + if (std::strcmp(exception.what(), "UNIQUE constraint failed: action.name") != 0) + { + throw; + } + } + } +} + void obelisk::KnowledgeBase::addFacts(std::vector& facts) { for (auto& fact : facts) @@ -150,9 +163,14 @@ void obelisk::KnowledgeBase::getVerb(obelisk::Verb& verb) verb.selectByName(dbConnection_); } +void obelisk::KnowledgeBase::getAction(obelisk::Action& action) +{ + action.selectByName(dbConnection_); +} + void obelisk::KnowledgeBase::getFact(obelisk::Fact& fact) { - fact.selectByName(dbConnection_); + fact.selectById(dbConnection_); } void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) diff --git a/src/knowledge_base.h b/src/lib/knowledge_base.h similarity index 85% rename from src/knowledge_base.h rename to src/lib/knowledge_base.h index 05cced7..7fb2bde 100644 --- a/src/knowledge_base.h +++ b/src/lib/knowledge_base.h @@ -1,8 +1,11 @@ #ifndef OBELISK_KNOWLEDGE_BASE_H #define OBELISK_KNOWLEDGE_BASE_H +#include "models/action.h" #include "models/entity.h" #include "models/fact.h" +#include "models/rule.h" +#include "models/suggest_action.h" #include "models/verb.h" #include @@ -23,7 +26,7 @@ namespace obelisk { private: /** - * @brief The filename of the opened knowledge base. + * @brief The filename of the opened KnowledgeBase. * */ const char* filename_; @@ -61,7 +64,7 @@ namespace obelisk * * @param[in] filename The name of the file to save the knowledge * base as. - * @param[in] flags The flags to open the knowledge base with. + * @param[in] flags The flags to open the KnowledgeBase with. */ KnowledgeBase(const char* filename, int flags); @@ -79,12 +82,12 @@ namespace obelisk /** * @brief Destroy the KnowledgeBase object. * - * This will close the opened knowledge base before destroying it. + * This will close the opened KnowledgeBase before destroying it. */ ~KnowledgeBase(); /** - * @brief Add entities to the knowledge base. + * @brief Add entities to the KnowledgeBase. * * @param[in,out] entities The entities to add. If the insert is * successful it will have a row ID, if not the ID will be 0. @@ -92,13 +95,21 @@ namespace obelisk void addEntities(std::vector& entities); /** - * @brief Add verbs to the knowledge base. + * @brief Add verbs to the KnowledgeBase. * * @param[in,out] verbs The verbs to add. If the insert is * successful it will have a row ID, if not the ID will be 0. */ void addVerbs(std::vector& verbs); + /** + * @brief Add actions to the KnowledgeBase. + * + * @param[in,out] actions The actions to add. If the insert is + * successful it will have a row ID, if nto the ID will be 0. + */ + void addActions(std::vector& actions); + /** * @brief Add facts to the database. * @@ -123,6 +134,14 @@ namespace obelisk */ void getVerb(obelisk::Verb& verb); + /** + * @brief Get an action based on the ID it contains. + * + * @param[in] action The Action object should contain just the ID + * and the rest will be filled in. + */ + void getAction(obelisk::Action& action); + /** * @brief Get a fact object based on the ID it contains. * diff --git a/src/lib/meson.build b/src/lib/meson.build index a81ecb3..30dff63 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -9,7 +9,8 @@ configure_file(input : 'version.h.in', subdir('models') obelisk_lib_sources = files( - 'obelisk.cpp' + 'obelisk.cpp', + 'knowledge_base.cpp' ) obelisk_lib_sources += obelisk_model_sources diff --git a/src/lib/models/action.cpp b/src/lib/models/action.cpp index bb6ed06..d6d3e10 100644 --- a/src/lib/models/action.cpp +++ b/src/lib/models/action.cpp @@ -1,4 +1,5 @@ #include "models/action.h" +#include "models/error.h" const char* obelisk::Action::createTable() { @@ -11,6 +12,131 @@ const char* obelisk::Action::createTable() )"; } +void obelisk::Action::selectByName(sqlite3* dbConnection) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, "SELECT id, name FROM action WHERE name=?", -1, &ppStmt, nullptr); + + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_STATIC); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseSizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseRangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseMemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_step(ppStmt); + switch (result) + { + case SQLITE_DONE : + // no rows in the database + break; + case SQLITE_ROW : + setId(sqlite3_column_int(ppStmt, 0)); + setName((char*) sqlite3_column_text(ppStmt, 1)); + break; + case SQLITE_BUSY : + throw obelisk::DatabaseBusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseMisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_finalize(ppStmt); + + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } +} + +void obelisk::Action::insert(sqlite3* dbConnection) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, "INSERT INTO action (name) VALUES (?)", -1, &ppStmt, nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_TRANSIENT); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseSizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseRangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseMemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_step(ppStmt); + switch (result) + { + case SQLITE_DONE : + setId((int) sqlite3_last_insert_rowid(dbConnection)); + sqlite3_set_last_insert_rowid(dbConnection, 0); + break; + case SQLITE_CONSTRAINT : + throw obelisk::DatabaseConstraintException(sqlite3_errmsg(dbConnection)); + case SQLITE_BUSY : + throw obelisk::DatabaseBusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseMisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_finalize(ppStmt); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } +} + int& obelisk::Action::getId() { return id_; diff --git a/src/lib/models/action.h b/src/lib/models/action.h index 7f22827..2d06c03 100644 --- a/src/lib/models/action.h +++ b/src/lib/models/action.h @@ -1,6 +1,8 @@ #ifndef OBELISK_MODELS_ACTION_H #define OBELISK_MODELS_ACTION_H +#include + #include namespace obelisk @@ -14,7 +16,7 @@ namespace obelisk { private: /** - * @brief The ID of the Action in the knowledge base. + * @brief The ID of the Action in the KnowledgeBase. * */ int id_; @@ -71,7 +73,7 @@ namespace obelisk } /** - * @brief Create the Action table in the knowledge base. + * @brief Create the Action table in the KnowledgeBase. * * @return const char* Returns the query used to create the table. */ @@ -104,6 +106,22 @@ namespace obelisk * @param[in] name The name of the Action. */ void setName(std::string name); + + /** + * @brief Select an Action from the datbase based on the object + * name. + * + * @param[in] dbConnection The database connection to use. + */ + void selectByName(sqlite3* dbConnection); + + /** + * @brief Insert an Action into the KnowledgeBase based on the + * object's fields. + * + * @param[in] dbConnection The database connection to use. + */ + void insert(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/lib/models/entity.h b/src/lib/models/entity.h index c8e0fdf..3833f99 100644 --- a/src/lib/models/entity.h +++ b/src/lib/models/entity.h @@ -16,10 +16,11 @@ namespace obelisk { private: /** - * @brief The ID of the Entity. + * @brief The ID of the Entity in the KnowledgeBase. * */ int id_; + /** * @brief The name of the Entity. * @@ -72,7 +73,7 @@ namespace obelisk } /** - * @brief Create the table in the knowledge base. + * @brief Create the table in the KnowledgeBase. * * @return const char* Returns the query used to create the table. */ @@ -107,7 +108,7 @@ namespace obelisk void setName(std::string name); /** - * @brief Select an Entity from the database based on the object's + * @brief Select an Entity from the KnowledgeBase based on the object's * name. * * @param[in] dbConnection The database connection to use. @@ -115,7 +116,7 @@ namespace obelisk void selectByName(sqlite3* dbConnection); /** - * @brief Insert an Entity into the database based on the object's + * @brief Insert an Entity into the KnowledgeBase based on the object's * fields. * * @param[in] dbConnection The database connection to use. diff --git a/src/lib/models/error.h b/src/lib/models/error.h index 1e1af7d..86d0987 100644 --- a/src/lib/models/error.h +++ b/src/lib/models/error.h @@ -6,77 +6,152 @@ namespace obelisk { + /** + * @brief Exception thrown by database models. + * + */ class DatabaseException : public std::exception { protected: + /** + * @brief The error message describing the exception. + * + */ std::string errorMessage_; public: + /** + * @brief Construct a new DatabaseException object. + * + */ DatabaseException() : errorMessage_("an unknown error ocurred") { } + /** + * @brief Construct a new DatabaseException object. + * + * @param[in] errorCode The error code that came from sqlite. + */ DatabaseException(const int errorCode) : errorMessage_("database error " + std::to_string(errorCode) + " ocurred") { } + /** + * @brief Construct a new DatabaseException object. + * + * @param[in] errorMessage The error message to describe the + * exception. + */ DatabaseException(const std::string& errorMessage) : errorMessage_(errorMessage) { } + /** + * @brief Retreive the exception message as a C type string. + * + * @return const char* The error message. + */ virtual const char* what() const noexcept { return errorMessage_.c_str(); } + /** + * @brief Set the error message. + * + * @param[in] errorMessage The error message. + */ virtual void setErrorMessage(const std::string errorMessage) { errorMessage_ = errorMessage; } }; + /** + * @brief Exception thrown if the string or blob size exceeds sqlite's + * limits. + * + */ class DatabaseSizeException : public obelisk::DatabaseException { public: + /** + * @brief Construct a new DatabaseSizeException object. + * + */ DatabaseSizeException() { setErrorMessage("size of string or blob exceeds limits"); } }; + /** + * @brief Exception thrown if the index used it out of range. + * + */ class DatabaseRangeException : public obelisk::DatabaseException { public: + /** + * @brief Construct a new DatabaseRangeException object. + * + */ DatabaseRangeException() { setErrorMessage("parameter index is out of range"); } }; + /** + * @brief Exception thrown if there is not enough memory to perform the + * operation. + * + */ class DatabaseMemoryException : public obelisk::DatabaseException { public: + /** + * @brief Construct a new DatabaseMemoryException object. + * + */ DatabaseMemoryException() { setErrorMessage("not enough memory for operation"); } }; + /** + * @brief Exception thrown if the database was busy. + * + */ class DatabaseBusyException : public obelisk::DatabaseException { public: + /** + * @brief Construct a new DatabaseBusyException object. + * + */ DatabaseBusyException() { setErrorMessage("database was busy and operation was not performed"); } }; + /** + * @brief Exception thrown if there is a misuse of the databse. + * + */ class DatabaseMisuseException : public obelisk::DatabaseException { public: + /** + * @brief Construct a new DatabaseMisuseException object. + * + */ DatabaseMisuseException() { @@ -84,14 +159,28 @@ namespace obelisk } }; + /** + * @brief Exception thrown if a constraint was violated. + * + */ class DatabaseConstraintException : public obelisk::DatabaseException { public: + /** + * @brief Construct a new DatabaseConstraintException object. + * + */ DatabaseConstraintException() { setErrorMessage("a constraint exception occurred"); } + /** + * @brief Construct a new DatabaseConstraintException object. + * + * @param[in] errorMessage The error message to send when the + * constraint is violated. + */ DatabaseConstraintException(const std::string& errorMessage) { setErrorMessage(errorMessage); diff --git a/src/lib/models/fact.cpp b/src/lib/models/fact.cpp index 1b79f94..c6092ea 100644 --- a/src/lib/models/fact.cpp +++ b/src/lib/models/fact.cpp @@ -19,7 +19,7 @@ const char* obelisk::Fact::createTable() )"; } -void obelisk::Fact::selectByName(sqlite3* dbConnection) +void obelisk::Fact::selectById(sqlite3* dbConnection) { if (dbConnection == nullptr) { diff --git a/src/lib/models/fact.h b/src/lib/models/fact.h index ecb95d0..4e6b821 100644 --- a/src/lib/models/fact.h +++ b/src/lib/models/fact.h @@ -9,16 +9,50 @@ namespace obelisk { + /** + * @brief The Fact model represents truth in the releationship between two + * entities separated by a verb. + * + */ class Fact { private: + /** + * @brief The ID of the Fact in the KnowledgeBase. + * + */ int id_; + + /** + * @brief The Entity from the left side of the expression. + * + */ obelisk::Entity leftEntity_; + + /** + * @brief The Entity from the right side of the expression. + * + */ obelisk::Entity rightEntity_; + + /** + * @brief The Verb that represents the relationship in the + * expression. + * + */ obelisk::Verb verb_; + + /** + * @brief Whether or not the fact is considered true or not. + * + */ bool isTrue_; public: + /** + * @brief Construct a new Fact object. + * + */ Fact() : id_(0), leftEntity_(), @@ -28,6 +62,11 @@ namespace obelisk { } + /** + * @brief Construct a new Fact object. + * + * @param[in] id The ID of the Fact in the KnowledgeBase. + */ Fact(int id) : id_(id), leftEntity_(), @@ -37,6 +76,16 @@ namespace obelisk { } + /** + * @brief Construct a new Fact object. + * + * @param[in] leftEntity The Entity on the left side of the + * expression. + * @param[in] rightEntity The Entity on the right side of the + * expression. + * @param[in] verb The Verb separating the entities. + * @param[in] isTrue Whether or not the fact is true. + */ Fact(obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb, bool isTrue = false) : id_(0), leftEntity_(leftEntity), @@ -46,6 +95,17 @@ namespace obelisk { } + /** + * @brief Construct a new Fact object. + * + * @param[in] id The ID of the Fact in the KnowledgeBase. + * @param[in] leftEntity The Entity on the left side of the + * expression. + * @param[in] rightEntity The Entity on the right side of the + * expression. + * @param[in] verb The Verb separating the entities. + * @param[in] isTrue Whether or not the fact is true. + */ Fact(int id, obelisk::Entity leftEntity, obelisk::Entity rightEntity, @@ -59,24 +119,97 @@ namespace obelisk { } + /** + * @brief Create the Fact table in the KnowledgeBase. + * + * @return const char* Returns the query used to create the table. + */ static const char* createTable(); + /** + * @brief Get the ID of the Fact + * + * @return int& Returns the ID. + */ int& getId(); + + /** + * @brief Set the ID of the Fact. + * + * @param[in] id Set the ID of the Fact. + */ void setId(int id); + /** + * @brief Get the left Entity object. + * + * @return Entity& The left Entity. + */ Entity& getLeftEntity(); + + /** + * @brief Set the left Entity object. + * + * @param[in] leftEntity The left Entity to set. + */ void setLeftEntity(obelisk::Entity leftEntity); + /** + * @brief Get the right Entity object. + * + * @return Entity& The right Entity. + */ Entity& getRightEntity(); - void setRightEntity(obelisk::Entity leftEntity); + /** + * @brief Set the right Entity object. + * + * @param[in] rightEntity The right Entity to set. + */ + void setRightEntity(obelisk::Entity rightEntity); + + /** + * @brief Get the Verb object. + * + * @return Verb& The Verb. + */ Verb& getVerb(); + + /** + * @brief Set the Verb object. + * + * @param[in] verb The Verb. + */ void setVerb(obelisk::Verb verb); + /** + * @brief Gets the isTrue value. + * + * @return true If the Fact is considered true. + * @return false If the Fact is considered false. + */ bool& getIsTrue(); + + /** + * @brief Set the Fact as true or false. + * + * @param[in] isTrue Whether or not the Fact is true. + */ void setIsTrue(bool isTrue); - void selectByName(sqlite3* dbConnection); + /** + * @brief Select the Fact from the KnowledgeBase by IDs of the + * sub-objects. + * + * @param[in] dbConnection The database connection to use. + */ + void selectById(sqlite3* dbConnection); + + /** + * @brief Insert the Fact into the KnowledgeBase. + * + * @param[in] dbConnection The database connection to use. + */ void insert(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/lib/models/rule.h b/src/lib/models/rule.h index ae75a85..5d3b23e 100644 --- a/src/lib/models/rule.h +++ b/src/lib/models/rule.h @@ -7,14 +7,36 @@ namespace obelisk { + /** + * @brief The Rule model represents a truth relation between 2 Facts. + * + */ class Rule { private: + /** + * @brief The ID of the Rule in the KnowledgeBase. + * + */ int id_; + + /** + * @brief The Fact that depends on the Fact reason being true. + * + */ obelisk::Fact fact_; + + /** + * @brief The Fact that makes the other Fact true or false. + * + */ obelisk::Fact reason_; public: + /** + * @brief Construct a new Rule object. + * + */ Rule() : id_(0), fact_(), @@ -22,6 +44,11 @@ namespace obelisk { } + /** + * @brief Construct a new Rule object. + * + * @param[in] id The ID of the Rule in the KnowledgeBase. + */ Rule(int id) : id_(id), fact_(), @@ -29,6 +56,12 @@ namespace obelisk { } + /** + * @brief Construct a new Rule object. + * + * @param[in] fact The Fact. + * @param[in] reason The reason Fact. + */ Rule(obelisk::Fact fact, obelisk::Fact reason) : id_(0), fact_(fact), @@ -36,6 +69,13 @@ namespace obelisk { } + /** + * @brief Construct a new Rule object. + * + * @param[in] id The ID of the Rule. + * @param[in] fact The Fact. + * @param[in] reason The reason Fact. + */ Rule(int id, obelisk::Fact fact, obelisk::Fact reason) : id_(id), fact_(fact), @@ -43,15 +83,53 @@ namespace obelisk { } + /** + * @brief Create the Rule table in the KnowledgeBase. + * + * @return const char* Returns the query used to create the table. + */ static const char* createTable(); + /** + * @brief Get the ID of the Rule. + * + * @return int& The ID. + */ int& getId(); + + /** + * @brief Set the ID of the Rule. + * + * @param[in] id The ID. + */ void setId(int id); + /** + * @brief Get the Fact object. + * + * @return obelisk::Fact& The Fact. + */ obelisk::Fact& getFact(); + + /** + * @brief Set the Fact object. + * + * @param[in] fact The Fact. + */ void setFact(obelisk::Fact fact); + /** + * @brief Get the reason Fact object. + * + * @return obelisk::Fact& The reason Fact. + */ obelisk::Fact& getReason(); + + /** + * @brief Set the reason Fact object. + * + * @param[in] reason The reason Fact. + */ void setReason(obelisk::Fact reason); }; } // namespace obelisk diff --git a/src/lib/models/suggest_action.cpp b/src/lib/models/suggest_action.cpp index 838a33b..f92a1c7 100644 --- a/src/lib/models/suggest_action.cpp +++ b/src/lib/models/suggest_action.cpp @@ -1,3 +1,4 @@ +#include "models/error.h" #include "models/suggest_action.h" const char* obelisk::SuggestAction::createTable() @@ -17,6 +18,215 @@ const char* obelisk::SuggestAction::createTable() )"; } +void obelisk::SuggestAction::selectById(sqlite3* dbConnection) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, + "SELECT id, fact, true_action, false_action FROM suggest_action WHERE (fact=? AND true_action=? AND false_action=?)", + -1, + &ppStmt, + nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result = sqlite3_bind_int(ppStmt, 1, getFact().getId()); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseSizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseRangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseMemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_bind_int(ppStmt, 2, getTrueAction().getId()); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseSizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseRangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseMemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_bind_int(ppStmt, 3, getFalseAction().getId()); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseSizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseRangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseMemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_step(ppStmt); + switch (result) + { + case SQLITE_DONE : + // no rows in the database + break; + case SQLITE_ROW : + setId(sqlite3_column_int(ppStmt, 0)); + getFact().setId(sqlite3_column_int(ppStmt, 1)); + getTrueAction().setId(sqlite3_column_int(ppStmt, 2)); + getFalseAction().setId(sqlite3_column_int(ppStmt, 3)); + break; + case SQLITE_BUSY : + throw obelisk::DatabaseBusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseMisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_finalize(ppStmt); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } +} + +void obelisk::SuggestAction::insert(sqlite3* dbConnection) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, + "INSERT INTO suggest_action (fact, true_action, false_action) VALUES (?, ?, ?)", + -1, + &ppStmt, + nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result = sqlite3_bind_int(ppStmt, 1, getFact().getId()); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseSizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseRangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseMemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_bind_int(ppStmt, 2, getTrueAction().getId()); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseSizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseRangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseMemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_bind_int(ppStmt, 3, getFalseAction().getId()); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseSizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseRangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseMemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_step(ppStmt); + switch (result) + { + case SQLITE_DONE : + setId((int) sqlite3_last_insert_rowid(dbConnection)); + sqlite3_set_last_insert_rowid(dbConnection, 0); + break; + case SQLITE_CONSTRAINT : + throw obelisk::DatabaseConstraintException(sqlite3_errmsg(dbConnection)); + case SQLITE_BUSY : + throw obelisk::DatabaseBusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseMisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_finalize(ppStmt); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } +} + int& obelisk::SuggestAction::getId() { return id_; diff --git a/src/lib/models/suggest_action.h b/src/lib/models/suggest_action.h index 4e298f3..e5e18b7 100644 --- a/src/lib/models/suggest_action.h +++ b/src/lib/models/suggest_action.h @@ -8,15 +8,43 @@ namespace obelisk { + /** + * @brief The SuggestAction model representas the actions to take depending + * on if the Fact is true or false. + * + */ class SuggestAction { private: + /** + * @brief The ID of the SuggestAction. + * + */ int id_; + + /** + * @brief The Fact to check the truth of. + * + */ obelisk::Fact fact_; + + /** + * @brief The Action to take if the Fact is true. + * + */ obelisk::Action trueAction_; + + /** + * @brief The Action to take if the Fact is false. + * + */ obelisk::Action falseAction_; public: + /** + * @brief Construct a new SuggestAction object. + * + */ SuggestAction() : id_(0), fact_(), @@ -25,6 +53,11 @@ namespace obelisk { } + /** + * @brief Construct a new SuggestAction object. + * + * @param[in] id The ID of the SuggestAction in the KnowledgeBase. + */ SuggestAction(int id) : id_(id), fact_(), @@ -33,6 +66,13 @@ namespace obelisk { } + /** + * @brief Construct a new SuggestAction object. + * + * @param[in] fact The Fact. + * @param[in] trueAction The true Action. + * @param[in] falseAction The false Action. + */ SuggestAction(obelisk::Fact fact, obelisk::Action trueAction, obelisk::Action falseAction) : id_(0), fact_(fact), @@ -41,6 +81,14 @@ namespace obelisk { } + /** + * @brief Construct a new SuggestAction object. + * + * @param[in] id The ID of the SuggestAction in the KnowledgeBase. + * @param[in] fact The Fact. + * @param[in] trueAction The true Action. + * @param[in] falseAction The false Action. + */ SuggestAction(int id, obelisk::Fact fact, obelisk::Action trueAction, obelisk::Action falseAction) : id_(id), fact_(fact), @@ -49,19 +97,83 @@ namespace obelisk { } + /** + * @brief Create the SuggestAction table in the database. + * + * @return const char* Returns the query used to create the table. + */ static const char* createTable(); + /** + * @brief Get the ID of the SuggestAction. + * + * @return int& Returns the ID. + */ int& getId(); + + /** + * @brief Set the ID of the SuggestAction. + * + * @param[in] id The new ID. + */ void setId(int id); + /** + * @brief Get the Fact object. + * + * @return obelisk::Fact& Returns the Fact. + */ obelisk::Fact& getFact(); + + /** + * @brief Set the Fact object. + * + * @param[in] fact The new Fact. + */ void setFact(obelisk::Fact fact); + /** + * @brief Get the true Action object. + * + * @return obelisk::Action& Returns the true Action. + */ obelisk::Action& getTrueAction(); + + /** + * @brief Set the true Action object. + * + * @param[in] trueAction The new true Action. + */ void setTrueAction(obelisk::Action trueAction); + /** + * @brief Get the false Action object. + * + * @return obelisk::Action& Returns the false Action. + */ obelisk::Action& getFalseAction(); + + /** + * @brief Set the false Action object. + * + * @param[in] falseAction The new false Action. + */ void setFalseAction(obelisk::Action falseAction); + + /** + * @brief Select the SuggestAction from the KnowledgeBase by IDs of the + * sub-objects. + * + * @param[in] dbConnection The database connection to use. + */ + void selectById(sqlite3* dbConnection); + + /** + * @brief Insert the SuggestAction into the KnowledgeBase. + * + * @param[in] dbConnection The database connection to use. + */ + void insert(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/lib/models/verb.h b/src/lib/models/verb.h index 969a438..150e86f 100644 --- a/src/lib/models/verb.h +++ b/src/lib/models/verb.h @@ -16,7 +16,7 @@ namespace obelisk { private: /** - * @brief The ID of the Verb in the knowledge base. + * @brief The ID of the Verb in the KnowledgeBase. * */ int id_; diff --git a/src/lib/obelisk.cpp b/src/lib/obelisk.cpp index b9b4a3f..e5b979a 100644 --- a/src/lib/obelisk.cpp +++ b/src/lib/obelisk.cpp @@ -1,8 +1,6 @@ #include "include/obelisk.h" #include "version.h" -// TODO: Move the models to the library then link the compiler to the library - std::string obelisk::Obelisk::getVersion() { return obelisk::version; diff --git a/src/meson.build b/src/meson.build index a97b4ea..5d5b978 100644 --- a/src/meson.build +++ b/src/meson.build @@ -3,8 +3,7 @@ subdir('lib') obelisk_sources = files( 'main.cpp', 'lexer.cpp', - 'parser.cpp', - 'knowledge_base.cpp' + 'parser.cpp' ) sqlite3 = dependency('sqlite3') diff --git a/src/parser.cpp b/src/parser.cpp index 9ac6841..46f4a31 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -389,7 +389,7 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) break; } - if (getLexer()->getIdentifier() == "or") + if (getLexer()->getIdentifier() == "else") { getNextToken(); getAction = true; @@ -406,7 +406,7 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) suggestAction.setFact( obelisk::Fact(obelisk::Entity(leftEntity), obelisk::Entity(rightEntity), obelisk::Verb(verb))); suggestAction.setTrueAction(obelisk::Action(trueAction)); - suggestAction.setTrueAction(obelisk::Action(falseAction)); + suggestAction.setFalseAction(obelisk::Action(falseAction)); } void obelisk::Parser::parseRule(std::vector& rules) @@ -547,6 +547,8 @@ void obelisk::Parser::handleAction(std::unique_ptr& kb) insertEntity(kb, suggestAction.getFact().getRightEntity()); insertVerb(kb, suggestAction.getFact().getVerb()); insertFact(kb, suggestAction.getFact()); + insertAction(kb, suggestAction.getTrueAction()); + insertAction(kb, suggestAction.getFalseAction()); // TODO: insert the actions, then insert the suggested action } @@ -647,6 +649,23 @@ void obelisk::Parser::insertVerb(std::unique_ptr& kb, ob } } +void obelisk::Parser::insertAction(std::unique_ptr& kb, obelisk::Action& action) +{ + std::vector actions {action}; + kb->addActions(actions); + action = std::move(actions.front()); + + // the id was not inserted, so check if it exists in the database + if (action.getId() == 0) + { + kb->getAction(action); + if (action.getId() == 0) + { + throw obelisk::ParserException("action could not be inserted into the database"); + } + } +} + void obelisk::Parser::insertFact(std::unique_ptr& kb, obelisk::Fact& fact) { std::vector facts {fact}; diff --git a/src/parser.h b/src/parser.h index bea0dd9..7c6ac1d 100644 --- a/src/parser.h +++ b/src/parser.h @@ -63,6 +63,7 @@ namespace obelisk void insertEntity(std::unique_ptr& kb, obelisk::Entity& entity); void insertVerb(std::unique_ptr& kb, obelisk::Verb& verb); + void insertAction(std::unique_ptr& kb, obelisk::Action& action); void insertFact(std::unique_ptr& kb, obelisk::Fact& fact); };