From ab83a1626bd15e6f6743cba5c97454d2f4a58f2e Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Wed, 15 Feb 2023 21:49:53 -0300 Subject: [PATCH 01/27] fix fact db structure --- src/models/fact.cpp | 34 ++++++++++++++++++++++++++++++++-- src/models/fact.h | 20 ++++++++++++++------ src/parser.cpp | 2 +- 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/models/fact.cpp b/src/models/fact.cpp index 8011bcc..69c1feb 100644 --- a/src/models/fact.cpp +++ b/src/models/fact.cpp @@ -29,7 +29,7 @@ void obelisk::Fact::selectFact(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; auto result = sqlite3_prepare_v2(dbConnection, - "SELECT id, left_entity, right_entity, verb FROM fact WHERE (left_entity=? AND right_entity=? AND verb=?)", + "SELECT id, left_entity, right_entity, verb, is_true FROM fact WHERE (left_entity=? AND right_entity=? AND verb=?)", -1, &ppStmt, nullptr); @@ -106,6 +106,7 @@ void obelisk::Fact::selectFact(sqlite3* dbConnection) getLeftEntity().setId(sqlite3_column_int(ppStmt, 1)); getRightEntity().setId(sqlite3_column_int(ppStmt, 2)); getVerb().setId(sqlite3_column_int(ppStmt, 3)); + setIsTrue(sqlite3_column_int(ppStmt, 4)); break; case SQLITE_BUSY : throw obelisk::DatabaseBusyException(); @@ -135,7 +136,7 @@ void obelisk::Fact::insertFact(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; auto result = sqlite3_prepare_v2(dbConnection, - "INSERT INTO fact (left_entity, right_entity, verb) VALUES (?, ?, ?)", + "INSERT INTO fact (left_entity, right_entity, verb, is_true) VALUES (?, ?, ?, ?)", -1, &ppStmt, nullptr); @@ -201,6 +202,25 @@ void obelisk::Fact::insertFact(sqlite3* dbConnection) break; } + result = sqlite3_bind_int(ppStmt, 4, getIsTrue()); + 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) { @@ -267,3 +287,13 @@ void obelisk::Fact::setVerb(obelisk::Verb verb) { verb_ = verb; } + +bool& obelisk::Fact::getIsTrue() +{ + return isTrue_; +} + +void obelisk::Fact::setIsTrue(bool isTrue) +{ + isTrue_ = isTrue; +} diff --git a/src/models/fact.h b/src/models/fact.h index 22486d3..5f5bfc4 100644 --- a/src/models/fact.h +++ b/src/models/fact.h @@ -16,13 +16,15 @@ namespace obelisk obelisk::Entity leftEntity_; obelisk::Entity rightEntity_; obelisk::Verb verb_; + bool isTrue_; public: Fact() : id_(0), leftEntity_(), rightEntity_(), - verb_() + verb_(), + isTrue_(0) { } @@ -30,23 +32,26 @@ namespace obelisk id_(id), leftEntity_(), rightEntity_(), - verb_() + verb_(), + isTrue_(0) { } - Fact(obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb) : + Fact(obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb, bool isTrue) : id_(0), leftEntity_(leftEntity), rightEntity_(rightEntity), - verb_(verb) + verb_(verb), + isTrue_(isTrue) { } - Fact(int id, obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb) : + Fact(int id, obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb, bool isTrue) : id_(id), leftEntity_(leftEntity), rightEntity_(rightEntity), - verb_(verb) + verb_(verb), + isTrue_(isTrue) { } @@ -64,6 +69,9 @@ namespace obelisk Verb& getVerb(); void setVerb(obelisk::Verb verb); + bool& getIsTrue(); + void setIsTrue(bool isTrue); + void selectFact(sqlite3* dbConnection); void insertFact(sqlite3* dbConnection); }; diff --git a/src/parser.cpp b/src/parser.cpp index c420a70..fa7a554 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -336,7 +336,7 @@ void obelisk::Parser::parseFact(std::vector& facts) for (auto& rightEntity : rightEntities) { facts.push_back( - obelisk::Fact(obelisk::Entity(leftEntity), obelisk::Entity(rightEntity), obelisk::Verb(verb))); + obelisk::Fact(obelisk::Entity(leftEntity), obelisk::Entity(rightEntity), obelisk::Verb(verb), true)); } } } From 3dcfdf23e85892455d8266533b82ef8372c1db6a Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Wed, 15 Feb 2023 22:56:07 -0300 Subject: [PATCH 02/27] remove old comments --- src/parser.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index fa7a554..b30c75b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -214,18 +214,14 @@ std::unique_ptr obelisk::Parser::parseExtern() return parsePrototype(); } -//action("martin" is "dangerous" then "avoid" or "ignore"); std::unique_ptr obelisk::Parser::parseAction() { } -//rule("chris" and "martin" is "happy" if "chris" plays "playstation"); std::unique_ptr obelisk::Parser::parseRule() { } -// fact("chris cromer" and "martin" and "Isabella" can "program" and "speak english"); -// fact("" and "martin") void obelisk::Parser::parseFact(std::vector& facts) { std::stack syntax; @@ -238,8 +234,6 @@ void obelisk::Parser::parseFact(std::vector& facts) syntax.push('('); - // (" - bool getEntity {true}; std::vector leftEntities; std::vector rightEntities; @@ -437,5 +431,3 @@ void obelisk::Parser::handleFact(std::unique_ptr& kb) } } } - -// fact("chris cromer" and "martin" and "Isabella" can "program" and "speak english"); From 5a884fa2a0caceb70d6b90f05067bcea3d452d01 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 00:32:50 -0300 Subject: [PATCH 03/27] make isTrue optional and default to false --- src/models/fact.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/models/fact.h b/src/models/fact.h index 5f5bfc4..94c4f17 100644 --- a/src/models/fact.h +++ b/src/models/fact.h @@ -37,7 +37,7 @@ namespace obelisk { } - Fact(obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb, bool isTrue) : + Fact(obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb, bool isTrue = false) : id_(0), leftEntity_(leftEntity), rightEntity_(rightEntity), @@ -46,7 +46,11 @@ namespace obelisk { } - Fact(int id, obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb, bool isTrue) : + Fact(int id, + obelisk::Entity leftEntity, + obelisk::Entity rightEntity, + obelisk::Verb verb, + bool isTrue = false) : id_(id), leftEntity_(leftEntity), rightEntity_(rightEntity), From 6062004f67b42aab3f5c17c271f45259e8e6754a Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 00:34:45 -0300 Subject: [PATCH 04/27] add missing comments to verb header --- src/models/verb.h | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/models/verb.h b/src/models/verb.h index 058d1d1..5e9a042 100644 --- a/src/models/verb.h +++ b/src/models/verb.h @@ -7,46 +7,118 @@ namespace obelisk { + /** + * @brief The Verb model represents a verb which is used to connnect + * entities. + * + */ class Verb { private: + /** + * @brief The ID of the Verb in the knowledge base. + * + */ int id_; + + /** + * @brief The name of the Verb. + * + */ std::string name_; public: + /** + * @brief Construct a new Verb object. + * + */ Verb() : id_(0), name_("") { } + /** + * @brief Construct a new Verb object. + * + * @param[in] id The ID of the Verb. + */ Verb(int id) : id_(id), name_("") { } + /** + * @brief Construct a new Verb object. + * + * @param[in] name The name of the Verb. + */ Verb(std::string name) : id_(0), name_(name) { } + /** + * @brief Construct a new Verb object. + * + * @param[in] id The ID of the Verb. + * @param[in] name The name of the Verb. + */ Verb(int id, std::string name) : id_(id), name_(name) { } + /** + * @brief Create the Verb 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 Verb. + * + * @return int& Returns the ID. + */ int& getId(); + + /** + * @brief Set the ID of the Verb. + * + * @param[in] id Set the ID of the Verb. + */ void setId(int id); + /** + * @brief Get the name of the Verb. + * + * @return std::string& The Verb name. + */ std::string& getName(); + + /** + * @brief Set the name of the Verb. + * + * @param[in] name The Verb name. + */ void setName(std::string name); + /** + * @brief Select a verb by name from the KnowledgeBase. + * + * @param[in] dbConnection The database connection to use. + */ void selectVerb(sqlite3* dbConnection); + + /** + * @brief Insert a new verb into the KnowledgeBase. + * + * @param[in] dbConnection The database connection to use. + */ void insertVerb(sqlite3* dbConnection); }; } // namespace obelisk From bc93f42c00a4d0eef0cfb4e0340fa8d1a46a8076 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 00:35:29 -0300 Subject: [PATCH 05/27] parse suggested actions --- src/obelisk.cpp | 2 +- src/parser.cpp | 364 +++++++++++++++++++++++++++++++++++++++--------- src/parser.h | 13 +- 3 files changed, 310 insertions(+), 69 deletions(-) diff --git a/src/obelisk.cpp b/src/obelisk.cpp index 8d64044..6b6a71d 100644 --- a/src/obelisk.cpp +++ b/src/obelisk.cpp @@ -89,7 +89,7 @@ int obelisk::mainLoop(const std::vector& sourceFiles, const std::st // parser->handleRule(); break; case obelisk::Lexer::kTokenAction : - // parser->handleAction(); + parser->handleAction(kb); break; default : parser->getNextToken(); diff --git a/src/parser.cpp b/src/parser.cpp index b30c75b..9ac6841 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,9 +1,6 @@ #include "ast/call_expression_ast.h" #include "ast/number_expression_ast.h" #include "ast/variable_expression_ast.h" -#include "models/entity.h" -#include "models/fact.h" -#include "models/verb.h" #include "parser.h" #include @@ -214,11 +211,205 @@ std::unique_ptr obelisk::Parser::parseExtern() return parsePrototype(); } -std::unique_ptr obelisk::Parser::parseAction() +void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) { + std::stack syntax; + + getNextToken(); + if (getCurrentToken() != '(') + { + throw obelisk::ParserException("expected '(' but got '" + std::to_string(getCurrentToken()) + "'"); + } + + syntax.push('('); + + getNextToken(); + if (getLexer()->getIdentifier() != "if") + { + throw obelisk::ParserException("expected 'if' but got '" + getLexer()->getIdentifier() + "'"); + } + + bool getEntity {true}; + std::string leftEntity {""}; + std::string rightEntity {""}; + std::string trueAction {""}; + std::string falseAction {""}; + std::string entityName {""}; + std::string verb {""}; + getNextToken(); + + // get the entity side of statement + while (true) + { + if (getEntity) + { + if (getCurrentToken() == '"') + { + if (syntax.top() != '"') + { + // open a double quote + syntax.push('"'); + getNextToken(); + } + else if (syntax.top() == '"') + { + // close a double quote + syntax.pop(); + if (verb == "") + { + leftEntity = entityName; + } + else + { + rightEntity = entityName; + } + entityName = ""; + getEntity = false; + getNextToken(); + continue; + } + } + + if (syntax.top() == '"') + { + if (entityName != "") + { + entityName += " "; + } + entityName += getLexer()->getIdentifier(); + } + getNextToken(); + } + else + { + if (getCurrentToken() == ')') + { + throw obelisk::ParserException("unexpected ')'"); + } + + if (getCurrentToken() == '"') + { + throw obelisk::ParserException("unexpected '\"'"); + break; + } + + if (getLexer()->getIdentifier() == "and") + { + getNextToken(); + getEntity = true; + continue; + } + else if (getLexer()->getIdentifier() == "then") + { + break; + } + else + { + verb = getLexer()->getIdentifier(); + // TODO: make sure verb is alphabetic + getEntity = true; + continue; + } + } + } + + // get the action side of statement + bool getAction {true}; + while (true) + { + if (getAction) + { + if (getCurrentToken() == '"') + { + if (syntax.top() != '"') + { + // open a double quote + syntax.push('"'); + getNextToken(); + } + else if (syntax.top() == '"') + { + // close a double quote + syntax.pop(); + if (trueAction == "") + { + trueAction = entityName; + } + else + { + falseAction = entityName; + } + entityName = ""; + getAction = false; + getNextToken(); + continue; + } + } + + if (syntax.top() == '"') + { + if (entityName != "") + { + entityName += " "; + } + entityName += getLexer()->getIdentifier(); + } + getNextToken(); + } + else + { + if (getCurrentToken() == ')') + { + // closing parenthesis found, make sure we have everything needed + if (syntax.top() != '(') + { + throw obelisk::ParserException("unexpected ')'"); + } + else + { + syntax.pop(); + } + + if (trueAction == "") + { + throw obelisk::ParserException("missing true action"); + } + + if (falseAction == "") + { + throw obelisk::ParserException("missing false action"); + } + + break; + } + + if (getCurrentToken() == '"') + { + throw obelisk::ParserException("unexpected '\"'"); + break; + } + + if (getLexer()->getIdentifier() == "or") + { + getNextToken(); + getAction = true; + continue; + } + else + { + getAction = true; + continue; + } + } + } + + suggestAction.setFact( + obelisk::Fact(obelisk::Entity(leftEntity), obelisk::Entity(rightEntity), obelisk::Verb(verb))); + suggestAction.setTrueAction(obelisk::Action(trueAction)); + suggestAction.setTrueAction(obelisk::Action(falseAction)); } -std::unique_ptr obelisk::Parser::parseRule() +void obelisk::Parser::parseRule(std::vector& rules) { } @@ -240,7 +431,7 @@ void obelisk::Parser::parseFact(std::vector& facts) std::string entityName {""}; std::string verb {""}; getNextToken(); - while (true) //left side of fact + while (true) { if (getEntity) { @@ -286,6 +477,15 @@ void obelisk::Parser::parseFact(std::vector& facts) if (getCurrentToken() == ')') { // closing parenthesis found, make sure we have everything needed + if (syntax.top() != '(') + { + throw obelisk::ParserException("unexpected ')'"); + } + else + { + syntax.pop(); + } + if (verb == "") { throw obelisk::ParserException("verb is empty"); @@ -300,6 +500,7 @@ void obelisk::Parser::parseFact(std::vector& facts) { throw obelisk::ParserException("missing right side entities"); } + break; } @@ -337,6 +538,22 @@ void obelisk::Parser::parseFact(std::vector& facts) void obelisk::Parser::handleAction(std::unique_ptr& kb) { + obelisk::SuggestAction suggestAction; + parseAction(suggestAction); + + try + { + insertEntity(kb, suggestAction.getFact().getLeftEntity()); + insertEntity(kb, suggestAction.getFact().getRightEntity()); + insertVerb(kb, suggestAction.getFact().getVerb()); + insertFact(kb, suggestAction.getFact()); + + // TODO: insert the actions, then insert the suggested action + } + catch (obelisk::ParserException& exception) + { + throw; + } } void obelisk::Parser::handleRule(std::unique_ptr& kb) @@ -346,88 +563,103 @@ void obelisk::Parser::handleRule(std::unique_ptr& kb) void obelisk::Parser::handleFact(std::unique_ptr& kb) { std::vector facts; - parseFact(facts); + try + { + parseFact(facts); + } + catch (obelisk::ParserException& exception) + { + throw; + } int verbId = 0; for (auto& fact : facts) { - std::vector entities {fact.getLeftEntity()}; - kb->addEntities(entities); - fact.setLeftEntity(entities.front()); - - // the id was not inserted, so check if it exists in the database - if (fact.getLeftEntity().getId() == 0) + try { - obelisk::Entity entity = fact.getLeftEntity(); - kb->getEntity(entity); - if (entity.getId() == 0) - { - throw obelisk::ParserException("left entity could not be inserted into the database"); - } - else - { - fact.setLeftEntity(entity); - } + insertEntity(kb, fact.getLeftEntity()); + insertEntity(kb, fact.getRightEntity()); } - - entities = {fact.getRightEntity()}; - kb->addEntities(entities); - fact.setRightEntity(entities.front()); - - if (fact.getRightEntity().getId() == 0) + catch (obelisk::ParserException& exception) { - obelisk::Entity entity = fact.getRightEntity(); - kb->getEntity(entity); - if (entity.getId() == 0) - { - throw obelisk::ParserException("right entity could not be inserted into the database"); - } - else - { - fact.setRightEntity(entity); - } + throw; } if (verbId == 0) { - std::vector verbs = {fact.getVerb()}; - kb->addVerbs(verbs); - if (verbs.front().getId() != 0) + try { - fact.setVerb(verbs.front()); - verbId = fact.getVerb().getId(); + insertVerb(kb, fact.getVerb()); } - else + catch (obelisk::ParserException& exception) { - obelisk::Verb verb = fact.getVerb(); - kb->getVerb(verb); - if (verb.getId() == 0) - { - throw obelisk::ParserException("verb could not be inserted into the database"); - } - else - { - fact.setVerb(verb); - verbId = fact.getVerb().getId(); - } + throw; } + verbId = fact.getVerb().getId(); } else { fact.getVerb().setId(verbId); } - std::vector facts {fact}; - kb->addFacts(facts); - fact = facts.front(); - - if (fact.getId() == 0) + try { - kb->getFact(fact); - if (fact.getId() == 0) - { - throw obelisk::ParserException("fact could not be inserted into the database"); - } + insertFact(kb, fact); + } + catch (obelisk::ParserException& exception) + { + throw; + } + } +} + +void obelisk::Parser::insertEntity(std::unique_ptr& kb, obelisk::Entity& entity) +{ + std::vector entities {entity}; + kb->addEntities(entities); + entity = std::move(entities.front()); + + // the id was not inserted, so check if it exists in the database + if (entity.getId() == 0) + { + kb->getEntity(entity); + if (entity.getId() == 0) + { + throw obelisk::ParserException("entity could not be inserted into the database"); + } + } +} + +void obelisk::Parser::insertVerb(std::unique_ptr& kb, obelisk::Verb& verb) +{ + std::vector verbs {verb}; + kb->addVerbs(verbs); + verb = std::move(verbs.front()); + + // the id was not inserted, so check if it exists in the database + if (verb.getId() == 0) + { + kb->getVerb(verb); + if (verb.getId() == 0) + { + throw obelisk::ParserException("verb could not be inserted into the database"); + } + } +} + +void obelisk::Parser::insertFact(std::unique_ptr& kb, obelisk::Fact& fact) +{ + std::vector facts {fact}; + kb->addFacts(facts); + fact = std::move(facts.front()); + + // the id was not inserted, so check if it exists in the database + if (fact.getId() == 0) + { + kb->getFact(fact); + if (fact.getId() == 0) + { + throw obelisk::ParserException("fact could not be inserted into the database"); } } } diff --git a/src/parser.h b/src/parser.h index 19b4a8b..bea0dd9 100644 --- a/src/parser.h +++ b/src/parser.h @@ -6,7 +6,12 @@ #include "ast/prototype_ast.h" #include "knowledge_base.h" #include "lexer.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 @@ -32,8 +37,8 @@ namespace obelisk std::unique_ptr parseDefinition(); std::unique_ptr parseTopLevelExpression(); std::unique_ptr parseExtern(); - std::unique_ptr parseAction(); - std::unique_ptr parseRule(); + void parseAction(obelisk::SuggestAction& suggestAction); + void parseRule(std::vector& rules); void parseFact(std::vector& facts); public: @@ -55,6 +60,10 @@ namespace obelisk void handleAction(std::unique_ptr& kb); void handleRule(std::unique_ptr& kb); void handleFact(std::unique_ptr& kb); + + void insertEntity(std::unique_ptr& kb, obelisk::Entity& entity); + void insertVerb(std::unique_ptr& kb, obelisk::Verb& verb); + void insertFact(std::unique_ptr& kb, obelisk::Fact& fact); }; class ParserException : public std::exception From f7b62e46454268aff9d252d4822f38a06638f083 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 00:40:43 -0300 Subject: [PATCH 06/27] refactor select and insert methods to be less redundant --- src/knowledge_base.cpp | 12 ++++++------ src/models/entity.cpp | 4 ++-- src/models/entity.h | 4 ++-- src/models/fact.cpp | 4 ++-- src/models/fact.h | 4 ++-- src/models/verb.cpp | 4 ++-- src/models/verb.h | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/knowledge_base.cpp b/src/knowledge_base.cpp index f9e3ae9..cd4aa6f 100644 --- a/src/knowledge_base.cpp +++ b/src/knowledge_base.cpp @@ -87,7 +87,7 @@ void obelisk::KnowledgeBase::addEntities(std::vector& entities) { try { - entity.insertEntity(dbConnection_); + entity.insert(dbConnection_); } catch (obelisk::DatabaseConstraintException& exception) { @@ -106,7 +106,7 @@ void obelisk::KnowledgeBase::addVerbs(std::vector& verbs) { try { - verb.insertVerb(dbConnection_); + verb.insert(dbConnection_); } catch (obelisk::DatabaseConstraintException& exception) { @@ -125,7 +125,7 @@ void obelisk::KnowledgeBase::addFacts(std::vector& facts) { try { - fact.insertFact(dbConnection_); + fact.insert(dbConnection_); } catch (obelisk::DatabaseConstraintException& exception) { @@ -142,17 +142,17 @@ void obelisk::KnowledgeBase::addFacts(std::vector& facts) void obelisk::KnowledgeBase::getEntity(obelisk::Entity& entity) { - entity.selectEntity(dbConnection_); + entity.select(dbConnection_); } void obelisk::KnowledgeBase::getVerb(obelisk::Verb& verb) { - verb.selectVerb(dbConnection_); + verb.select(dbConnection_); } void obelisk::KnowledgeBase::getFact(obelisk::Fact& fact) { - fact.selectFact(dbConnection_); + fact.select(dbConnection_); } void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) diff --git a/src/models/entity.cpp b/src/models/entity.cpp index f051372..1ca6f7c 100644 --- a/src/models/entity.cpp +++ b/src/models/entity.cpp @@ -12,7 +12,7 @@ const char* obelisk::Entity::createTable() )"; } -void obelisk::Entity::selectEntity(sqlite3* dbConnection) +void obelisk::Entity::select(sqlite3* dbConnection) { if (dbConnection == nullptr) { @@ -76,7 +76,7 @@ void obelisk::Entity::selectEntity(sqlite3* dbConnection) } } -void obelisk::Entity::insertEntity(sqlite3* dbConnection) +void obelisk::Entity::insert(sqlite3* dbConnection) { if (dbConnection == nullptr) { diff --git a/src/models/entity.h b/src/models/entity.h index 7ede503..1796dd9 100644 --- a/src/models/entity.h +++ b/src/models/entity.h @@ -112,7 +112,7 @@ namespace obelisk * * @param[in] dbConnection The database connection to use. */ - void selectEntity(sqlite3* dbConnection); + void select(sqlite3* dbConnection); /** * @brief Insert an Entity into the database based on the object's @@ -120,7 +120,7 @@ namespace obelisk * * @param[in] dbConnection The database connection to use. */ - void insertEntity(sqlite3* dbConnection); + void insert(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/models/fact.cpp b/src/models/fact.cpp index 69c1feb..cddc7e3 100644 --- a/src/models/fact.cpp +++ b/src/models/fact.cpp @@ -19,7 +19,7 @@ const char* obelisk::Fact::createTable() )"; } -void obelisk::Fact::selectFact(sqlite3* dbConnection) +void obelisk::Fact::select(sqlite3* dbConnection) { if (dbConnection == nullptr) { @@ -126,7 +126,7 @@ void obelisk::Fact::selectFact(sqlite3* dbConnection) } } -void obelisk::Fact::insertFact(sqlite3* dbConnection) +void obelisk::Fact::insert(sqlite3* dbConnection) { if (dbConnection == nullptr) { diff --git a/src/models/fact.h b/src/models/fact.h index 94c4f17..76233fa 100644 --- a/src/models/fact.h +++ b/src/models/fact.h @@ -76,8 +76,8 @@ namespace obelisk bool& getIsTrue(); void setIsTrue(bool isTrue); - void selectFact(sqlite3* dbConnection); - void insertFact(sqlite3* dbConnection); + void select(sqlite3* dbConnection); + void insert(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/models/verb.cpp b/src/models/verb.cpp index e47d5cd..b91503c 100644 --- a/src/models/verb.cpp +++ b/src/models/verb.cpp @@ -14,7 +14,7 @@ const char* obelisk::Verb::createTable() )"; } -void obelisk::Verb::selectVerb(sqlite3* dbConnection) +void obelisk::Verb::select(sqlite3* dbConnection) { if (dbConnection == nullptr) { @@ -76,7 +76,7 @@ void obelisk::Verb::selectVerb(sqlite3* dbConnection) } } -void obelisk::Verb::insertVerb(sqlite3* dbConnection) +void obelisk::Verb::insert(sqlite3* dbConnection) { if (dbConnection == nullptr) { diff --git a/src/models/verb.h b/src/models/verb.h index 5e9a042..be6599e 100644 --- a/src/models/verb.h +++ b/src/models/verb.h @@ -112,14 +112,14 @@ namespace obelisk * * @param[in] dbConnection The database connection to use. */ - void selectVerb(sqlite3* dbConnection); + void select(sqlite3* dbConnection); /** * @brief Insert a new verb into the KnowledgeBase. * * @param[in] dbConnection The database connection to use. */ - void insertVerb(sqlite3* dbConnection); + void insert(sqlite3* dbConnection); }; } // namespace obelisk From 7b515f1f07aa6badba550b0b95c9da07826576af Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 00:44:32 -0300 Subject: [PATCH 07/27] make it more obvious that name is used in the select --- src/knowledge_base.cpp | 6 +++--- src/models/entity.cpp | 2 +- src/models/entity.h | 4 ++-- src/models/fact.cpp | 2 +- src/models/fact.h | 2 +- src/models/verb.cpp | 2 +- src/models/verb.h | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/knowledge_base.cpp b/src/knowledge_base.cpp index cd4aa6f..4227231 100644 --- a/src/knowledge_base.cpp +++ b/src/knowledge_base.cpp @@ -142,17 +142,17 @@ void obelisk::KnowledgeBase::addFacts(std::vector& facts) void obelisk::KnowledgeBase::getEntity(obelisk::Entity& entity) { - entity.select(dbConnection_); + entity.selectByName(dbConnection_); } void obelisk::KnowledgeBase::getVerb(obelisk::Verb& verb) { - verb.select(dbConnection_); + verb.selectByName(dbConnection_); } void obelisk::KnowledgeBase::getFact(obelisk::Fact& fact) { - fact.select(dbConnection_); + fact.selectByName(dbConnection_); } void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) diff --git a/src/models/entity.cpp b/src/models/entity.cpp index 1ca6f7c..8d9250f 100644 --- a/src/models/entity.cpp +++ b/src/models/entity.cpp @@ -12,7 +12,7 @@ const char* obelisk::Entity::createTable() )"; } -void obelisk::Entity::select(sqlite3* dbConnection) +void obelisk::Entity::selectByName(sqlite3* dbConnection) { if (dbConnection == nullptr) { diff --git a/src/models/entity.h b/src/models/entity.h index 1796dd9..c8e0fdf 100644 --- a/src/models/entity.h +++ b/src/models/entity.h @@ -108,11 +108,11 @@ namespace obelisk /** * @brief Select an Entity from the database based on the object's - * ID. + * name. * * @param[in] dbConnection The database connection to use. */ - void select(sqlite3* dbConnection); + void selectByName(sqlite3* dbConnection); /** * @brief Insert an Entity into the database based on the object's diff --git a/src/models/fact.cpp b/src/models/fact.cpp index cddc7e3..1b79f94 100644 --- a/src/models/fact.cpp +++ b/src/models/fact.cpp @@ -19,7 +19,7 @@ const char* obelisk::Fact::createTable() )"; } -void obelisk::Fact::select(sqlite3* dbConnection) +void obelisk::Fact::selectByName(sqlite3* dbConnection) { if (dbConnection == nullptr) { diff --git a/src/models/fact.h b/src/models/fact.h index 76233fa..ecb95d0 100644 --- a/src/models/fact.h +++ b/src/models/fact.h @@ -76,7 +76,7 @@ namespace obelisk bool& getIsTrue(); void setIsTrue(bool isTrue); - void select(sqlite3* dbConnection); + void selectByName(sqlite3* dbConnection); void insert(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/models/verb.cpp b/src/models/verb.cpp index b91503c..61b46c1 100644 --- a/src/models/verb.cpp +++ b/src/models/verb.cpp @@ -14,7 +14,7 @@ const char* obelisk::Verb::createTable() )"; } -void obelisk::Verb::select(sqlite3* dbConnection) +void obelisk::Verb::selectByName(sqlite3* dbConnection) { if (dbConnection == nullptr) { diff --git a/src/models/verb.h b/src/models/verb.h index be6599e..969a438 100644 --- a/src/models/verb.h +++ b/src/models/verb.h @@ -112,7 +112,7 @@ namespace obelisk * * @param[in] dbConnection The database connection to use. */ - void select(sqlite3* dbConnection); + void selectByName(sqlite3* dbConnection); /** * @brief Insert a new verb into the KnowledgeBase. From f6e93a1b6702b8ee900d4b6f65278b5581755552 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 01:16:57 -0300 Subject: [PATCH 08/27] add the obelisk library source --- doc/doxygen.conf.in | 6 ++++-- include/version.h.in | 14 ++++++++++++++ lib/meson.build | 20 ++++++++++++++++++++ lib/version.h.in | 1 + meson.build | 3 +++ src/meson.build | 1 + src/version.h.in | 9 +-------- 7 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 include/version.h.in create mode 100644 lib/meson.build create mode 120000 lib/version.h.in mode change 100644 => 120000 src/version.h.in diff --git a/doc/doxygen.conf.in b/doc/doxygen.conf.in index 684e675..d268ea0 100644 --- a/doc/doxygen.conf.in +++ b/doc/doxygen.conf.in @@ -864,7 +864,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = @TOP_SRCDIR@/src +INPUT = @TOP_SRCDIR@/src @TOP_SRCDIR@/lib @TOP_SRCDIR@/include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -2237,7 +2237,9 @@ SEARCH_INCLUDES = YES INCLUDE_PATH = @TOP_SRCDIR@/src \ @TOP_SRCDIR@/src/ast \ - @TOP_SRCDIR@/src/models + @TOP_SRCDIR@/src/models \ + @TOP_SRCDIR@/lib \ + @TOP_SRCDIR@/include # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the diff --git a/include/version.h.in b/include/version.h.in new file mode 100644 index 0000000..1841178 --- /dev/null +++ b/include/version.h.in @@ -0,0 +1,14 @@ +namespace obelisk +{ + /** + * @brief The current version of obelisk. + * + */ + const std::string version = "@version@"; + + /** + * @brief The current library version of obelisk. + * + */ + const std::string soVersion = "@so_version@"; +} // namespace obelisk diff --git a/lib/meson.build b/lib/meson.build new file mode 100644 index 0000000..0e17871 --- /dev/null +++ b/lib/meson.build @@ -0,0 +1,20 @@ +conf_data = configuration_data() +conf_data.set('version', meson.project_version()) +conf_data.set('so_version', project_version_lib) +configure_file(input : 'version.h.in', + output : 'version.h', + configuration : conf_data +) + +obelisk_lib_sources = files( +) + +sqlite3 = dependency('sqlite3') + +library('obelisk', + obelisk_lib_sources, + dependencies : [sqlite3], + version : meson.project_version(), + soversion : project_version_lib, + install : true +) diff --git a/lib/version.h.in b/lib/version.h.in new file mode 120000 index 0000000..5c3fee4 --- /dev/null +++ b/lib/version.h.in @@ -0,0 +1 @@ +../include/version.h.in \ No newline at end of file diff --git a/meson.build b/meson.build index b8b534f..381f03d 100644 --- a/meson.build +++ b/meson.build @@ -9,6 +9,8 @@ project('obelisk', ] ) +project_version_lib = '0' + llvm = dependency('llvm', version: '>= 14.0.0', modules : ['core', 'target', 'mcjit', 'nativecodegen'], required : true, method: 'config-tool') cdata = configuration_data() @@ -27,4 +29,5 @@ if docs_enabled endif endif +subdir('lib') subdir('src') diff --git a/src/meson.build b/src/meson.build index 81adf96..051321b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,6 @@ conf_data = configuration_data() conf_data.set('version', meson.project_version()) +conf_data.set('so_version', project_version_lib) configure_file(input : 'version.h.in', output : 'version.h', configuration : conf_data diff --git a/src/version.h.in b/src/version.h.in deleted file mode 100644 index 7e82845..0000000 --- a/src/version.h.in +++ /dev/null @@ -1,8 +0,0 @@ -namespace obelisk -{ - /** - * @brief The current version of obelisk. - * - */ - std::string version = "@version@"; -} // namespace obelisk diff --git a/src/version.h.in b/src/version.h.in new file mode 120000 index 0000000..5c3fee4 --- /dev/null +++ b/src/version.h.in @@ -0,0 +1 @@ +../include/version.h.in \ No newline at end of file From bf5033c70d3ee04a7a0d923c0c4f22b4d9dc92c0 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 01:27:15 -0300 Subject: [PATCH 09/27] make lib and bin optional to only compile what is wanted --- meson.build | 11 +++++++++-- meson_options.txt | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 381f03d..b14aca0 100644 --- a/meson.build +++ b/meson.build @@ -29,5 +29,12 @@ if docs_enabled endif endif -subdir('lib') -subdir('src') +lib_enabled = get_option('lib') +if lib_enabled + subdir('lib') +endif + +bin_enabled = get_option('bin') +if bin_enabled + subdir('src') +endif diff --git a/meson_options.txt b/meson_options.txt index 4dff45a..69bb0db 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,13 @@ +option('bin', + type: 'boolean', + value: true, + description: 'Build the obelisk compiler' +) +option('lib', + type: 'boolean', + value: true, + description: 'Build the obelisk library' +) option('docs', type: 'boolean', value: true, From 48955026c48de0e04ce20dae3e603ef1c175d3fc Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 02:26:14 -0300 Subject: [PATCH 10/27] restructure library location --- lib/version.h.in | 1 - meson.build | 10 +--------- meson_options.txt | 10 ---------- src/lib/include/obelisk.h | 27 +++++++++++++++++++++++++++ {lib => src/lib}/meson.build | 11 ++++++++++- src/lib/obelisk.cpp | 14 ++++++++++++++ {include => src/lib}/version.h.in | 4 +++- src/meson.build | 10 ++-------- src/obelisk.cpp | 5 +++-- src/version.h.in | 1 - 10 files changed, 60 insertions(+), 33 deletions(-) delete mode 120000 lib/version.h.in create mode 100644 src/lib/include/obelisk.h rename {lib => src/lib}/meson.build (63%) create mode 100644 src/lib/obelisk.cpp rename {include => src/lib}/version.h.in (72%) delete mode 120000 src/version.h.in diff --git a/lib/version.h.in b/lib/version.h.in deleted file mode 120000 index 5c3fee4..0000000 --- a/lib/version.h.in +++ /dev/null @@ -1 +0,0 @@ -../include/version.h.in \ No newline at end of file diff --git a/meson.build b/meson.build index b14aca0..4f3a936 100644 --- a/meson.build +++ b/meson.build @@ -29,12 +29,4 @@ if docs_enabled endif endif -lib_enabled = get_option('lib') -if lib_enabled - subdir('lib') -endif - -bin_enabled = get_option('bin') -if bin_enabled - subdir('src') -endif +subdir('src') diff --git a/meson_options.txt b/meson_options.txt index 69bb0db..4dff45a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,13 +1,3 @@ -option('bin', - type: 'boolean', - value: true, - description: 'Build the obelisk compiler' -) -option('lib', - type: 'boolean', - value: true, - description: 'Build the obelisk library' -) option('docs', type: 'boolean', value: true, diff --git a/src/lib/include/obelisk.h b/src/lib/include/obelisk.h new file mode 100644 index 0000000..167183c --- /dev/null +++ b/src/lib/include/obelisk.h @@ -0,0 +1,27 @@ +#include + +/** + * @brief The obelisk namespace contains everything needed to compile obelisk. + * code. + * + */ +namespace obelisk +{ + class Obelisk + { + public: + /** + * @brief Get the obelisk version. + * + * @return std::string The version. + */ + std::string getVersion(); + + /** + * @brief Get the obelisk library so version. + * + * @return int The version. + */ + int getLibVersion(); + }; +} // namespace obelisk diff --git a/lib/meson.build b/src/lib/meson.build similarity index 63% rename from lib/meson.build rename to src/lib/meson.build index 0e17871..3cec326 100644 --- a/lib/meson.build +++ b/src/lib/meson.build @@ -7,14 +7,23 @@ configure_file(input : 'version.h.in', ) obelisk_lib_sources = files( + 'obelisk.cpp' ) sqlite3 = dependency('sqlite3') -library('obelisk', +incdirs = include_directories(['.', 'include']) +lib = library('obelisk', obelisk_lib_sources, + include_directories: incdirs, dependencies : [sqlite3], version : meson.project_version(), soversion : project_version_lib, install : true ) + +libobelisk_dep = declare_dependency( + include_directories : incdirs, + link_with : lib, + sources : obelisk_lib_sources +) diff --git a/src/lib/obelisk.cpp b/src/lib/obelisk.cpp new file mode 100644 index 0000000..b9b4a3f --- /dev/null +++ b/src/lib/obelisk.cpp @@ -0,0 +1,14 @@ +#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; +} + +int obelisk::Obelisk::getLibVersion() +{ + return obelisk::soVersion; +} diff --git a/include/version.h.in b/src/lib/version.h.in similarity index 72% rename from include/version.h.in rename to src/lib/version.h.in index 1841178..b6b0867 100644 --- a/include/version.h.in +++ b/src/lib/version.h.in @@ -10,5 +10,7 @@ namespace obelisk * @brief The current library version of obelisk. * */ - const std::string soVersion = "@so_version@"; + // clang-format off + const int soVersion = @so_version@; + // clang-format on } // namespace obelisk diff --git a/src/meson.build b/src/meson.build index 051321b..5f5900c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,10 +1,4 @@ -conf_data = configuration_data() -conf_data.set('version', meson.project_version()) -conf_data.set('so_version', project_version_lib) -configure_file(input : 'version.h.in', - output : 'version.h', - configuration : conf_data -) +subdir('lib') obelisk_sources = files( 'obelisk.cpp', @@ -29,7 +23,7 @@ link_args = ' ' + r.stdout().replace('\n', ' ') executable('obelisk', obelisk_sources, - dependencies : [sqlite3], + dependencies : [libobelisk_dep, sqlite3], cpp_args : cpp_args.split(), link_args : link_args.split(), install : true diff --git a/src/obelisk.cpp b/src/obelisk.cpp index 6b6a71d..f790ca9 100644 --- a/src/obelisk.cpp +++ b/src/obelisk.cpp @@ -1,8 +1,9 @@ #include "knowledge_base.h" #include "lexer.h" +#include "lib/include/obelisk.h" +#include "lib/version.h" #include "obelisk.h" #include "parser.h" -#include "version.h" #include #include @@ -123,7 +124,7 @@ int main(int argc, char** argv) return EXIT_SUCCESS; break; case 'v' : - std::cout << "obelisk " << obelisk::version << std::endl; + std::cout << "obelisk " << (new obelisk::Obelisk())->getVersion() << std::endl; return EXIT_SUCCESS; break; default : diff --git a/src/version.h.in b/src/version.h.in deleted file mode 120000 index 5c3fee4..0000000 --- a/src/version.h.in +++ /dev/null @@ -1 +0,0 @@ -../include/version.h.in \ No newline at end of file From 841914bae85c720b11327b214b36babfd26b6f50 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 02:27:24 -0300 Subject: [PATCH 11/27] print the version by accessing it directly --- src/obelisk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obelisk.cpp b/src/obelisk.cpp index f790ca9..602b692 100644 --- a/src/obelisk.cpp +++ b/src/obelisk.cpp @@ -124,7 +124,7 @@ int main(int argc, char** argv) return EXIT_SUCCESS; break; case 'v' : - std::cout << "obelisk " << (new obelisk::Obelisk())->getVersion() << std::endl; + std::cout << "obelisk " << obelisk::version << std::endl; return EXIT_SUCCESS; break; default : From 6ef6d14d183b206ff0d23c3bc947286183147928 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 02:36:16 -0300 Subject: [PATCH 12/27] restructure the link between the compiler and the library --- src/lib/meson.build | 2 +- src/{obelisk.cpp => main.cpp} | 4 ++-- src/{obelisk.h => main.h} | 0 src/meson.build | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/{obelisk.cpp => main.cpp} (98%) rename src/{obelisk.h => main.h} (100%) diff --git a/src/lib/meson.build b/src/lib/meson.build index 3cec326..95e1948 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -22,7 +22,7 @@ lib = library('obelisk', install : true ) -libobelisk_dep = declare_dependency( +libobelisk = declare_dependency( include_directories : incdirs, link_with : lib, sources : obelisk_lib_sources diff --git a/src/obelisk.cpp b/src/main.cpp similarity index 98% rename from src/obelisk.cpp rename to src/main.cpp index 602b692..edd4cc0 100644 --- a/src/obelisk.cpp +++ b/src/main.cpp @@ -1,9 +1,9 @@ #include "knowledge_base.h" #include "lexer.h" -#include "lib/include/obelisk.h" -#include "lib/version.h" +#include "main.h" #include "obelisk.h" #include "parser.h" +#include "version.h" #include #include diff --git a/src/obelisk.h b/src/main.h similarity index 100% rename from src/obelisk.h rename to src/main.h diff --git a/src/meson.build b/src/meson.build index 5f5900c..e6090d7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,7 +1,7 @@ subdir('lib') obelisk_sources = files( - 'obelisk.cpp', + 'main.cpp', 'lexer.cpp', 'parser.cpp', 'knowledge_base.cpp' @@ -23,7 +23,7 @@ link_args = ' ' + r.stdout().replace('\n', ' ') executable('obelisk', obelisk_sources, - dependencies : [libobelisk_dep, sqlite3], + dependencies : [libobelisk, sqlite3], cpp_args : cpp_args.split(), link_args : link_args.split(), install : true From 36b8889a6235377ef9e8ef92b63f67844f27b407 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 16 Feb 2023 22:58:20 -0300 Subject: [PATCH 13/27] move knowledge base into the library --- src/lib/meson.build | 4 ++++ src/{ => lib}/models/action.cpp | 0 src/{ => lib}/models/action.h | 0 src/{ => lib}/models/entity.cpp | 0 src/{ => lib}/models/entity.h | 0 src/{ => lib}/models/error.h | 0 src/{ => lib}/models/fact.cpp | 0 src/{ => lib}/models/fact.h | 0 src/{ => lib}/models/meson.build | 0 src/{ => lib}/models/rule.cpp | 0 src/{ => lib}/models/rule.h | 0 src/{ => lib}/models/suggest_action.cpp | 0 src/{ => lib}/models/suggest_action.h | 0 src/{ => lib}/models/verb.cpp | 0 src/{ => lib}/models/verb.h | 0 src/meson.build | 3 --- 16 files changed, 4 insertions(+), 3 deletions(-) rename src/{ => lib}/models/action.cpp (100%) rename src/{ => lib}/models/action.h (100%) rename src/{ => lib}/models/entity.cpp (100%) rename src/{ => lib}/models/entity.h (100%) rename src/{ => lib}/models/error.h (100%) rename src/{ => lib}/models/fact.cpp (100%) rename src/{ => lib}/models/fact.h (100%) rename src/{ => lib}/models/meson.build (100%) rename src/{ => lib}/models/rule.cpp (100%) rename src/{ => lib}/models/rule.h (100%) rename src/{ => lib}/models/suggest_action.cpp (100%) rename src/{ => lib}/models/suggest_action.h (100%) rename src/{ => lib}/models/verb.cpp (100%) rename src/{ => lib}/models/verb.h (100%) diff --git a/src/lib/meson.build b/src/lib/meson.build index 95e1948..a81ecb3 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -6,10 +6,14 @@ configure_file(input : 'version.h.in', configuration : conf_data ) +subdir('models') + obelisk_lib_sources = files( 'obelisk.cpp' ) +obelisk_lib_sources += obelisk_model_sources + sqlite3 = dependency('sqlite3') incdirs = include_directories(['.', 'include']) diff --git a/src/models/action.cpp b/src/lib/models/action.cpp similarity index 100% rename from src/models/action.cpp rename to src/lib/models/action.cpp diff --git a/src/models/action.h b/src/lib/models/action.h similarity index 100% rename from src/models/action.h rename to src/lib/models/action.h diff --git a/src/models/entity.cpp b/src/lib/models/entity.cpp similarity index 100% rename from src/models/entity.cpp rename to src/lib/models/entity.cpp diff --git a/src/models/entity.h b/src/lib/models/entity.h similarity index 100% rename from src/models/entity.h rename to src/lib/models/entity.h diff --git a/src/models/error.h b/src/lib/models/error.h similarity index 100% rename from src/models/error.h rename to src/lib/models/error.h diff --git a/src/models/fact.cpp b/src/lib/models/fact.cpp similarity index 100% rename from src/models/fact.cpp rename to src/lib/models/fact.cpp diff --git a/src/models/fact.h b/src/lib/models/fact.h similarity index 100% rename from src/models/fact.h rename to src/lib/models/fact.h diff --git a/src/models/meson.build b/src/lib/models/meson.build similarity index 100% rename from src/models/meson.build rename to src/lib/models/meson.build diff --git a/src/models/rule.cpp b/src/lib/models/rule.cpp similarity index 100% rename from src/models/rule.cpp rename to src/lib/models/rule.cpp diff --git a/src/models/rule.h b/src/lib/models/rule.h similarity index 100% rename from src/models/rule.h rename to src/lib/models/rule.h diff --git a/src/models/suggest_action.cpp b/src/lib/models/suggest_action.cpp similarity index 100% rename from src/models/suggest_action.cpp rename to src/lib/models/suggest_action.cpp diff --git a/src/models/suggest_action.h b/src/lib/models/suggest_action.h similarity index 100% rename from src/models/suggest_action.h rename to src/lib/models/suggest_action.h diff --git a/src/models/verb.cpp b/src/lib/models/verb.cpp similarity index 100% rename from src/models/verb.cpp rename to src/lib/models/verb.cpp diff --git a/src/models/verb.h b/src/lib/models/verb.h similarity index 100% rename from src/models/verb.h rename to src/lib/models/verb.h diff --git a/src/meson.build b/src/meson.build index e6090d7..a97b4ea 100644 --- a/src/meson.build +++ b/src/meson.build @@ -12,9 +12,6 @@ sqlite3 = dependency('sqlite3') subdir('ast') obelisk_sources += obelisk_ast_sources -subdir('models') -obelisk_sources += obelisk_model_sources - r = run_command('llvm-config', '--cppflags', check : true) cpp_args = ' ' + r.stdout().replace('\n', ' ').replace('-I', '-isystem') From b220b1c2151d4f048b2a019ed89212518a85e221 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Sat, 18 Feb 2023 20:14:59 -0300 Subject: [PATCH 14/27] update the documentation locations --- doc/doxygen.conf.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/doxygen.conf.in b/doc/doxygen.conf.in index d268ea0..2d007fc 100644 --- a/doc/doxygen.conf.in +++ b/doc/doxygen.conf.in @@ -864,7 +864,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = @TOP_SRCDIR@/src @TOP_SRCDIR@/lib @TOP_SRCDIR@/include +INPUT = @TOP_SRCDIR@/src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -2237,9 +2237,9 @@ SEARCH_INCLUDES = YES INCLUDE_PATH = @TOP_SRCDIR@/src \ @TOP_SRCDIR@/src/ast \ - @TOP_SRCDIR@/src/models \ - @TOP_SRCDIR@/lib \ - @TOP_SRCDIR@/include + @TOP_SRCDIR@/src/lib \ + @TOP_SRCDIR@/src/lib/models \ + @TOP_SRCDIR@/src/lib/include # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the From 91cce4c1703ee5e40be90f303b86ab3317e5b762 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Sat, 18 Feb 2023 21:06:42 -0300 Subject: [PATCH 15/27] 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); }; From d8ae5e1b95d0fb10d9254a3fe1bf95936fefcb7a Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Sat, 18 Feb 2023 21:32:05 -0300 Subject: [PATCH 16/27] finish inserting the suggested actions --- src/lib/knowledge_base.cpp | 26 ++++++++++++++++++++++++++ src/lib/knowledge_base.h | 29 +++++++++++++++++++++++------ src/parser.cpp | 21 +++++++++++++++++++-- src/parser.h | 2 ++ 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/lib/knowledge_base.cpp b/src/lib/knowledge_base.cpp index e0b48f6..37ba0a8 100644 --- a/src/lib/knowledge_base.cpp +++ b/src/lib/knowledge_base.cpp @@ -153,6 +153,27 @@ void obelisk::KnowledgeBase::addFacts(std::vector& facts) } } +void obelisk::KnowledgeBase::addSuggestActions(std::vector& suggestActions) +{ + for (auto& suggestAction : suggestActions) + { + try + { + suggestAction.insert(dbConnection_); + } + catch (obelisk::DatabaseConstraintException& exception) + { + // ignore unique constraint error + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: suggest_action.fact, suggest_action.true_action, suggest_action.false_action") + != 0) + { + throw; + } + } + } +} + void obelisk::KnowledgeBase::getEntity(obelisk::Entity& entity) { entity.selectByName(dbConnection_); @@ -173,6 +194,11 @@ void obelisk::KnowledgeBase::getFact(obelisk::Fact& fact) fact.selectById(dbConnection_); } +void obelisk::KnowledgeBase::getSuggestAction(obelisk::SuggestAction& suggestAction) +{ + suggestAction.selectById(dbConnection_); +} + void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) { result1 = (float) var; diff --git a/src/lib/knowledge_base.h b/src/lib/knowledge_base.h index 7fb2bde..ddb1165 100644 --- a/src/lib/knowledge_base.h +++ b/src/lib/knowledge_base.h @@ -111,7 +111,7 @@ namespace obelisk void addActions(std::vector& actions); /** - * @brief Add facts to the database. + * @brief Add facts to the KnowledgeBase. * * @param[in,out] facts The facts to add. If the insert is * successful it will have a row ID, if not the ID will be 0. @@ -119,7 +119,16 @@ namespace obelisk void addFacts(std::vector& facts); /** - * @brief Get an entity object based on the ID it contains. + * @brief Add suggested actions to the KnowledgeBase. + * + * @param[in,out] suggestActions The suggested actions to add. If + * the insert is successful it will have a row ID, if not the ID + * will be 0. + */ + void addSuggestActions(std::vector& suggestActions); + + /** + * @brief Get an Entity object based on the ID it contains. * * @param[in,out] entity The Entity object should contain just the * ID and the rest will be filled in. @@ -127,7 +136,7 @@ namespace obelisk void getEntity(obelisk::Entity& entity); /** - * @brief Get a verb object based on the ID it contains. + * @brief Get a Verb object based on the ID it contains. * * @param[in,out] verb The Verb object should contain just the ID * and the rest will be filled in. @@ -135,7 +144,7 @@ namespace obelisk void getVerb(obelisk::Verb& verb); /** - * @brief Get an action based on the ID it contains. + * @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. @@ -143,13 +152,21 @@ namespace obelisk void getAction(obelisk::Action& action); /** - * @brief Get a fact object based on the ID it contains. + * @brief Get a Fact object based on the ID it contains. * - * @param[in,out] fact The fact object should contain just the ID + * @param[in,out] fact The Fact object should contain just the ID * and the rest will be filled in. */ void getFact(obelisk::Fact& fact); + /** + * @brief Get a SuggestAction based on the ID it contains. + * + * @param[in,out] suggestAction The SuggestAction object should + * contain just the ID and the rest will be filled in. + */ + void getSuggestAction(obelisk::SuggestAction& suggestAction); + /** * @brief Take a float and divide it into 2 floats. * diff --git a/src/parser.cpp b/src/parser.cpp index 46f4a31..e9f0b2e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -549,8 +549,7 @@ void obelisk::Parser::handleAction(std::unique_ptr& kb) insertFact(kb, suggestAction.getFact()); insertAction(kb, suggestAction.getTrueAction()); insertAction(kb, suggestAction.getFalseAction()); - - // TODO: insert the actions, then insert the suggested action + insertSuggestAction(kb, suggestAction); } catch (obelisk::ParserException& exception) { @@ -682,3 +681,21 @@ void obelisk::Parser::insertFact(std::unique_ptr& kb, ob } } } + +void obelisk::Parser::insertSuggestAction(std::unique_ptr& kb, + obelisk::SuggestAction& suggestAction) +{ + std::vector suggestActions {suggestAction}; + kb->addSuggestActions(suggestActions); + suggestAction = std::move(suggestActions.front()); + + // the id was not inserted, so check if it exists in the database + if (suggestAction.getId() == 0) + { + kb->getSuggestAction(suggestAction); + if (suggestAction.getId() == 0) + { + throw obelisk::ParserException("suggest_action could not be inserted into the database"); + } + } +} diff --git a/src/parser.h b/src/parser.h index 7c6ac1d..7131a9e 100644 --- a/src/parser.h +++ b/src/parser.h @@ -65,6 +65,8 @@ namespace obelisk 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); + void insertSuggestAction(std::unique_ptr& kb, + obelisk::SuggestAction& suggestAction); }; class ParserException : public std::exception From 2f54f13b54c6e0dbad5d0befcac2bd354470d34a Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Sat, 18 Feb 2023 23:02:49 -0300 Subject: [PATCH 17/27] fix incorrect enum --- src/lexer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.cpp b/src/lexer.cpp index 32509b1..c941103 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -40,7 +40,7 @@ int obelisk::Lexer::getToken() if (getIdentifier() == "rule") { - return Token::kTokenFact; + return Token::kTokenRule; } if (getIdentifier() == "action") From ba8788af56b942d6087942a52828b89d02855f67 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Sun, 19 Feb 2023 01:03:35 -0300 Subject: [PATCH 18/27] implement rules --- src/lib/knowledge_base.cpp | 24 ++++ src/lib/knowledge_base.h | 16 +++ src/lib/models/rule.cpp | 168 ++++++++++++++++++++++ src/lib/models/rule.h | 15 ++ src/main.cpp | 30 +++- src/parser.cpp | 288 ++++++++++++++++++++++++++++++++++--- src/parser.h | 3 +- 7 files changed, 517 insertions(+), 27 deletions(-) diff --git a/src/lib/knowledge_base.cpp b/src/lib/knowledge_base.cpp index 37ba0a8..0e163de 100644 --- a/src/lib/knowledge_base.cpp +++ b/src/lib/knowledge_base.cpp @@ -174,6 +174,25 @@ void obelisk::KnowledgeBase::addSuggestActions(std::vector& rules) +{ + for (auto& rule : rules) + { + try + { + rule.insert(dbConnection_); + } + catch (obelisk::DatabaseConstraintException& exception) + { + // ignore unique constraint error + if (std::strcmp(exception.what(), "UNIQUE constraint failed: rule.fact, rule.reason") != 0) + { + throw; + } + } + } +} + void obelisk::KnowledgeBase::getEntity(obelisk::Entity& entity) { entity.selectByName(dbConnection_); @@ -199,6 +218,11 @@ void obelisk::KnowledgeBase::getSuggestAction(obelisk::SuggestAction& suggestAct suggestAction.selectById(dbConnection_); } +void obelisk::KnowledgeBase::getRule(obelisk::Rule& rule) +{ + rule.selectById(dbConnection_); +} + void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) { result1 = (float) var; diff --git a/src/lib/knowledge_base.h b/src/lib/knowledge_base.h index ddb1165..c1790a3 100644 --- a/src/lib/knowledge_base.h +++ b/src/lib/knowledge_base.h @@ -127,6 +127,14 @@ namespace obelisk */ void addSuggestActions(std::vector& suggestActions); + /** + * @brief Add rules to the KnowledgeBase. + * + * @param[in,out] rules The rules to add. If the insert is successful it + * will have a row ID, if not the ID will be 0. + */ + void addRules(std::vector& rules); + /** * @brief Get an Entity object based on the ID it contains. * @@ -167,6 +175,14 @@ namespace obelisk */ void getSuggestAction(obelisk::SuggestAction& suggestAction); + /** + * @brief Get a Rule based on the ID it contains. + * + * @param[in,out] rule The Rule object should contain just the ID + * and the rest will be filled in. + */ + void getRule(obelisk::Rule& rule); + /** * @brief Take a float and divide it into 2 floats. * diff --git a/src/lib/models/rule.cpp b/src/lib/models/rule.cpp index 7dfc4f5..26f9fd9 100644 --- a/src/lib/models/rule.cpp +++ b/src/lib/models/rule.cpp @@ -1,3 +1,4 @@ +#include "models/error.h" #include "models/rule.h" const char* obelisk::Rule::createTable() @@ -15,6 +16,173 @@ const char* obelisk::Rule::createTable() )"; } +void obelisk::Rule::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, reason FROM rule WHERE (fact=? AND reason=?)", + -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, getReason().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)); + getReason().setId(sqlite3_column_int(ppStmt, 2)); + 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::Rule::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 rule (fact, reason) 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, getReason().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::Rule::getId() { return id_; diff --git a/src/lib/models/rule.h b/src/lib/models/rule.h index 5d3b23e..e48189d 100644 --- a/src/lib/models/rule.h +++ b/src/lib/models/rule.h @@ -131,6 +131,21 @@ namespace obelisk * @param[in] reason The reason Fact. */ void setReason(obelisk::Fact reason); + + /** + * @brief Select the Rule from the KnowledgeBase by IDs of the + * sub-objects. + * + * @param[in] dbConnection The database connection to use. + */ + void selectById(sqlite3* dbConnection); + + /** + * @brief Insert the Rule into the KnowledgeBase. + * + * @param[in] dbConnection The database connection to use. + */ + void insert(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/main.cpp b/src/main.cpp index edd4cc0..e7f5e02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -84,13 +84,37 @@ int obelisk::mainLoop(const std::vector& sourceFiles, const std::st } break; case obelisk::Lexer::kTokenFact : - parser->handleFact(kb); + try + { + parser->handleFact(kb); + } + catch (obelisk::ParserException& exception) + { + std::cout << "Error: " << exception.what() << std::endl; + return EXIT_FAILURE; + } break; case obelisk::Lexer::kTokenRule : - // parser->handleRule(); + try + { + parser->handleRule(kb); + } + catch (obelisk::ParserException& exception) + { + std::cout << "Error: " << exception.what() << std::endl; + return EXIT_FAILURE; + } break; case obelisk::Lexer::kTokenAction : - parser->handleAction(kb); + try + { + parser->handleAction(kb); + } + catch (obelisk::ParserException& exception) + { + std::cout << "Error: " << exception.what() << std::endl; + return EXIT_FAILURE; + } break; default : parser->getNextToken(); diff --git a/src/parser.cpp b/src/parser.cpp index e9f0b2e..de93c3c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -257,14 +257,13 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) syntax.pop(); if (verb == "") { - leftEntity = entityName; + leftEntity = std::move(entityName); } else { - rightEntity = entityName; + rightEntity = std::move(entityName); } - entityName = ""; - getEntity = false; + getEntity = false; getNextToken(); continue; } @@ -290,23 +289,22 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) if (getCurrentToken() == '"') { throw obelisk::ParserException("unexpected '\"'"); - break; } - if (getLexer()->getIdentifier() == "and") - { - getNextToken(); - getEntity = true; - continue; - } - else if (getLexer()->getIdentifier() == "then") + if (getLexer()->getIdentifier() == "then") { break; } else { - verb = getLexer()->getIdentifier(); - // TODO: make sure verb is alphabetic + verb = getLexer()->getIdentifier(); + for (const auto& letter : verb) + { + if (!isalpha(letter)) + { + throw new obelisk::ParserException("non alphabetic symbol in verb"); + } + } getEntity = true; continue; } @@ -333,14 +331,13 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) syntax.pop(); if (trueAction == "") { - trueAction = entityName; + trueAction = std::move(entityName); } else { - falseAction = entityName; + falseAction = std::move(entityName); } - entityName = ""; - getAction = false; + getAction = false; getNextToken(); continue; } @@ -370,6 +367,21 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) syntax.pop(); } + if (leftEntity == "") + { + throw obelisk::ParserException("missing left entity"); + } + + if (rightEntity == "") + { + throw obelisk::ParserException("missing left entity"); + } + + if (verb == "") + { + throw obelisk::ParserException("missing verb"); + } + if (trueAction == "") { throw obelisk::ParserException("missing true action"); @@ -380,6 +392,12 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) throw obelisk::ParserException("missing false action"); } + getNextToken(); + if (getCurrentToken() != ';') + { + throw obelisk::ParserException("missing ';'"); + } + break; } @@ -409,8 +427,185 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) suggestAction.setFalseAction(obelisk::Action(falseAction)); } -void obelisk::Parser::parseRule(std::vector& rules) +void obelisk::Parser::parseRule(obelisk::Rule& rule) { + std::stack syntax; + + getNextToken(); + if (getCurrentToken() != '(') + { + throw obelisk::ParserException("expected '(' but got '" + std::to_string(getCurrentToken()) + "'"); + } + + syntax.push('('); + + bool getEntity {true}; + bool getReason {false}; + std::string leftEntity {""}; + std::string rightEntity {""}; + std::string verb {""}; + std::string leftReasonEntity {""}; + std::string rightReasonEntity {""}; + std::string reasonVerb {""}; + std::string entityName {""}; + getNextToken(); + + // get the entity side of statement + while (true) + { + if (getEntity) + { + if (getCurrentToken() == '"') + { + if (syntax.top() != '"') + { + // open a double quote + syntax.push('"'); + getNextToken(); + } + else if (syntax.top() == '"') + { + // close a double quote + syntax.pop(); + if (!getReason) + { + if (verb == "") + { + leftEntity = std::move(entityName); + } + else + { + rightEntity = std::move(entityName); + } + } + else + { + if (reasonVerb == "") + { + leftReasonEntity = std::move(entityName); + } + else + { + rightReasonEntity = std::move(entityName); + } + } + getEntity = false; + getNextToken(); + continue; + } + } + + if (syntax.top() == '"') + { + if (entityName != "") + { + entityName += " "; + } + entityName += getLexer()->getIdentifier(); + } + getNextToken(); + } + else + { + if (getCurrentToken() == ')') + { + // closing parenthesis found, make sure we have everything needed + if (syntax.top() != '(') + { + throw obelisk::ParserException("unexpected ')'"); + } + else + { + syntax.pop(); + } + + if (leftEntity == "") + { + throw obelisk::ParserException("missing left entity"); + } + + if (rightEntity == "") + { + throw obelisk::ParserException("missing left entity"); + } + + if (verb == "") + { + throw obelisk::ParserException("missing verb"); + } + + if (leftReasonEntity == "") + { + throw obelisk::ParserException("missing left reason entity"); + } + + if (rightReasonEntity == "") + { + throw obelisk::ParserException("missing right reason entity"); + } + + if (reasonVerb == "") + { + throw obelisk::ParserException("missing reason verb"); + } + + getNextToken(); + if (getCurrentToken() != ';') + { + throw obelisk::ParserException("missing ';'"); + } + + break; + } + + if (getCurrentToken() == '"') + { + throw obelisk::ParserException("unexpected '\"'"); + } + + if (getLexer()->getIdentifier() == "if") + { + getReason = true; + getEntity = true; + getNextToken(); + continue; + } + else + { + if (!getReason) + { + verb = getLexer()->getIdentifier(); + for (const auto& letter : verb) + { + if (!isalpha(letter)) + { + throw new obelisk::ParserException("non alphabetic symbol in verb"); + } + } + getEntity = true; + continue; + } + else + { + reasonVerb = getLexer()->getIdentifier(); + for (const auto& letter : reasonVerb) + { + if (!isalpha(letter)) + { + throw new obelisk::ParserException("non alphabetic symbol in verb"); + } + } + getEntity = true; + continue; + } + } + } + } + + rule.setFact(obelisk::Fact(obelisk::Entity(leftEntity), obelisk::Entity(rightEntity), obelisk::Verb(verb))); + rule.setReason(obelisk::Fact(obelisk::Entity(leftReasonEntity), + obelisk::Entity(rightReasonEntity), + obelisk::Verb(reasonVerb))); } void obelisk::Parser::parseFact(std::vector& facts) @@ -501,13 +696,18 @@ void obelisk::Parser::parseFact(std::vector& facts) throw obelisk::ParserException("missing right side entities"); } + getNextToken(); + if (getCurrentToken() != ';') + { + throw obelisk::ParserException("missing ';'"); + } + break; } if (getCurrentToken() == '"') { throw obelisk::ParserException("unexpected '\"'"); - break; } if (getLexer()->getIdentifier() == "and") @@ -518,8 +718,14 @@ void obelisk::Parser::parseFact(std::vector& facts) } else { - verb = getLexer()->getIdentifier(); - // TODO: make sure verb is alphabetic + verb = getLexer()->getIdentifier(); + for (const auto& letter : verb) + { + if (!isalpha(letter)) + { + throw new obelisk::ParserException("non alphabetic symbol in verb"); + } + } getEntity = true; continue; } @@ -539,10 +745,10 @@ void obelisk::Parser::parseFact(std::vector& facts) void obelisk::Parser::handleAction(std::unique_ptr& kb) { obelisk::SuggestAction suggestAction; - parseAction(suggestAction); try { + parseAction(suggestAction); insertEntity(kb, suggestAction.getFact().getLeftEntity()); insertEntity(kb, suggestAction.getFact().getRightEntity()); insertVerb(kb, suggestAction.getFact().getVerb()); @@ -559,6 +765,25 @@ void obelisk::Parser::handleAction(std::unique_ptr& kb) void obelisk::Parser::handleRule(std::unique_ptr& kb) { + obelisk::Rule rule; + + try + { + parseRule(rule); + insertEntity(kb, rule.getFact().getLeftEntity()); + insertEntity(kb, rule.getFact().getRightEntity()); + insertVerb(kb, rule.getFact().getVerb()); + insertFact(kb, rule.getFact()); + insertEntity(kb, rule.getReason().getLeftEntity()); + insertEntity(kb, rule.getReason().getRightEntity()); + insertVerb(kb, rule.getReason().getVerb()); + insertFact(kb, rule.getReason()); + insertRule(kb, rule); + } + catch (obelisk::ParserException& exception) + { + throw; + } } void obelisk::Parser::handleFact(std::unique_ptr& kb) @@ -699,3 +924,20 @@ void obelisk::Parser::insertSuggestAction(std::unique_ptr& kb, obelisk::Rule& rule) +{ + std::vector rules {rule}; + kb->addRules(rules); + rule = std::move(rules.front()); + + // the id was not inserted, so check if it exists in the database + if (rule.getId() == 0) + { + kb->getRule(rule); + if (rule.getId() == 0) + { + throw obelisk::ParserException("rule could not be inserted into the database"); + } + } +} diff --git a/src/parser.h b/src/parser.h index 7131a9e..13ad7ea 100644 --- a/src/parser.h +++ b/src/parser.h @@ -38,7 +38,7 @@ namespace obelisk std::unique_ptr parseTopLevelExpression(); std::unique_ptr parseExtern(); void parseAction(obelisk::SuggestAction& suggestAction); - void parseRule(std::vector& rules); + void parseRule(obelisk::Rule& rule); void parseFact(std::vector& facts); public: @@ -67,6 +67,7 @@ namespace obelisk void insertFact(std::unique_ptr& kb, obelisk::Fact& fact); void insertSuggestAction(std::unique_ptr& kb, obelisk::SuggestAction& suggestAction); + void insertRule(std::unique_ptr& kb, obelisk::Rule& rule); }; class ParserException : public std::exception From fc0984904cf57f382113af9c6837a2da881c7412 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Sun, 19 Feb 2023 01:09:54 -0300 Subject: [PATCH 19/27] add missing class doc --- src/lib/include/obelisk.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/include/obelisk.h b/src/lib/include/obelisk.h index 167183c..f379303 100644 --- a/src/lib/include/obelisk.h +++ b/src/lib/include/obelisk.h @@ -7,6 +7,11 @@ */ namespace obelisk { + /** + * @brief The obelisk library provides everything needed to consult the + * KnowledgeBase. + * + */ class Obelisk { public: From 88f17011ef39d06701b7b4e8d16312f53731d0bb Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Mon, 20 Feb 2023 09:11:10 -0300 Subject: [PATCH 20/27] finish knowledge base insertion --- src/lib/knowledge_base.cpp | 22 ++++ src/lib/knowledge_base.h | 15 +++ src/lib/models/fact.cpp | 221 +++++++++++++++++++++++++++---------- src/lib/models/fact.h | 8 ++ src/lib/models/rule.cpp | 63 +++++++++++ src/lib/models/rule.h | 9 ++ src/parser.cpp | 31 +++++- src/parser.h | 24 +++- 8 files changed, 330 insertions(+), 63 deletions(-) diff --git a/src/lib/knowledge_base.cpp b/src/lib/knowledge_base.cpp index 0e163de..f5f4170 100644 --- a/src/lib/knowledge_base.cpp +++ b/src/lib/knowledge_base.cpp @@ -223,6 +223,28 @@ void obelisk::KnowledgeBase::getRule(obelisk::Rule& rule) rule.selectById(dbConnection_); } +void obelisk::KnowledgeBase::checkRule(obelisk::Fact& fact) +{ + std::vector rules; + obelisk::Rule::selectByReason(dbConnection_, fact.getId(), rules); + for (auto& rule : rules) + { + auto reason = rule.getReason(); + getFact(reason); + if (reason.getIsTrue()) + { + auto updateFact = rule.getFact(); + updateFact.setIsTrue(true); + updateFact.updateIsTrue(dbConnection_); + } + } +} + +void obelisk::KnowledgeBase::updateIsTrue(obelisk::Fact& fact) +{ + fact.updateIsTrue(dbConnection_); +} + void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) { result1 = (float) var; diff --git a/src/lib/knowledge_base.h b/src/lib/knowledge_base.h index c1790a3..ac463d2 100644 --- a/src/lib/knowledge_base.h +++ b/src/lib/knowledge_base.h @@ -183,6 +183,21 @@ namespace obelisk */ void getRule(obelisk::Rule& rule); + /** + * @brief Check if a rule looks for this Fact, if so update its + * truth. + * + * @param[in,out] fact The Fact to check for existing rules. + */ + void checkRule(obelisk::Fact& fact); + + /** + * @brief Update the is true field in the KnowledgeBase. + * + * @param[in,out] fact The fact to update. + */ + void updateIsTrue(obelisk::Fact& fact); + /** * @brief Take a float and divide it into 2 floats. * diff --git a/src/lib/models/fact.cpp b/src/lib/models/fact.cpp index c6092ea..eafe955 100644 --- a/src/lib/models/fact.cpp +++ b/src/lib/models/fact.cpp @@ -28,71 +28,101 @@ void obelisk::Fact::selectById(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; - auto result = sqlite3_prepare_v2(dbConnection, - "SELECT id, left_entity, right_entity, verb, is_true FROM fact WHERE (left_entity=? AND right_entity=? AND verb=?)", - -1, - &ppStmt, - nullptr); + const char* query; + if (getId() == 0) + { + query + = "SELECT id, left_entity, right_entity, verb, is_true FROM fact WHERE (left_entity=? AND right_entity=? AND verb=?)"; + } + else + { + query = "SELECT id, left_entity, right_entity, verb, is_true FROM fact WHERE (id=?)"; + } + auto result = sqlite3_prepare_v2(dbConnection, query, -1, &ppStmt, nullptr); if (result != SQLITE_OK) { throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); } - result = sqlite3_bind_int(ppStmt, 1, getLeftEntity().getId()); - switch (result) + if (getId() == 0) { - 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, 1, getLeftEntity().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, getRightEntity().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, getRightEntity().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, getVerb().getId()); - switch (result) + result = sqlite3_bind_int(ppStmt, 3, getVerb().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; + } + } + else { - 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, 1, 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); @@ -248,6 +278,85 @@ void obelisk::Fact::insert(sqlite3* dbConnection) } } +void obelisk::Fact::updateIsTrue(sqlite3* dbConnection) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, "UPDATE fact SET is_true=? WHERE id=?", -1, &ppStmt, nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result = sqlite3_bind_int(ppStmt, 1, getIsTrue()); + 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, 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 : + // Row updated + 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::Fact::getId() { return id_; diff --git a/src/lib/models/fact.h b/src/lib/models/fact.h index 4e6b821..cc16b06 100644 --- a/src/lib/models/fact.h +++ b/src/lib/models/fact.h @@ -211,6 +211,14 @@ namespace obelisk * @param[in] dbConnection The database connection to use. */ void insert(sqlite3* dbConnection); + + /** + * @brief Update whether or not the fact is true in the + * KnowledgeBase. + * + * @param[in] dbConnection The database connection. + */ + void updateIsTrue(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/lib/models/rule.cpp b/src/lib/models/rule.cpp index 26f9fd9..9184b6c 100644 --- a/src/lib/models/rule.cpp +++ b/src/lib/models/rule.cpp @@ -183,6 +183,69 @@ void obelisk::Rule::insert(sqlite3* dbConnection) } } +void obelisk::Rule::selectByReason(sqlite3* dbConnection, int reasonId, std::vector& rules) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* ppStmt = nullptr; + + auto result + = sqlite3_prepare_v2(dbConnection, "SELECT id, fact, reason FROM rule WHERE (reason=?)", -1, &ppStmt, nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result = sqlite3_bind_int(ppStmt, 1, reasonId); + 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; + } + + while ((result = sqlite3_step(ppStmt)) != SQLITE_DONE) + { + switch (result) + { + case SQLITE_ROW : + rules.push_back(obelisk::Rule(sqlite3_column_int(ppStmt, 0), + obelisk::Fact(sqlite3_column_int(ppStmt, 1)), + obelisk::Fact(sqlite3_column_int(ppStmt, 2)))); + 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)); + } +} + int& obelisk::Rule::getId() { return id_; diff --git a/src/lib/models/rule.h b/src/lib/models/rule.h index e48189d..02f06fd 100644 --- a/src/lib/models/rule.h +++ b/src/lib/models/rule.h @@ -4,6 +4,7 @@ #include "models/fact.h" #include +#include namespace obelisk { @@ -140,6 +141,14 @@ namespace obelisk */ void selectById(sqlite3* dbConnection); + /** + * @brief Get the rules that match the reason. + * + * @param[in] dbConnection The database connection to use. + * @param[out] rules The rules to fill in from the database. + */ + static void selectByReason(sqlite3* dbConnection, int reasonId, std::vector& rules); + /** * @brief Insert the Rule into the KnowledgeBase. * diff --git a/src/parser.cpp b/src/parser.cpp index de93c3c..b3207c9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -770,14 +770,23 @@ void obelisk::Parser::handleRule(std::unique_ptr& kb) try { parseRule(rule); - insertEntity(kb, rule.getFact().getLeftEntity()); - insertEntity(kb, rule.getFact().getRightEntity()); - insertVerb(kb, rule.getFact().getVerb()); - insertFact(kb, rule.getFact()); + insertEntity(kb, rule.getReason().getLeftEntity()); insertEntity(kb, rule.getReason().getRightEntity()); insertVerb(kb, rule.getReason().getVerb()); insertFact(kb, rule.getReason()); + + // The rule is true, so the fact must be true to. + if (rule.getReason().getIsTrue()) + { + rule.getFact().setIsTrue(true); + } + + insertEntity(kb, rule.getFact().getLeftEntity()); + insertEntity(kb, rule.getFact().getRightEntity()); + insertVerb(kb, rule.getFact().getVerb()); + insertFact(kb, rule.getFact()); + insertRule(kb, rule); } catch (obelisk::ParserException& exception) @@ -830,12 +839,14 @@ void obelisk::Parser::handleFact(std::unique_ptr& kb) try { - insertFact(kb, fact); + insertFact(kb, fact, true); } catch (obelisk::ParserException& exception) { throw; } + + kb->checkRule(fact); } } @@ -890,7 +901,7 @@ void obelisk::Parser::insertAction(std::unique_ptr& kb, } } -void obelisk::Parser::insertFact(std::unique_ptr& kb, obelisk::Fact& fact) +void obelisk::Parser::insertFact(std::unique_ptr& kb, obelisk::Fact& fact, bool updateIsTrue) { std::vector facts {fact}; kb->addFacts(facts); @@ -904,6 +915,14 @@ void obelisk::Parser::insertFact(std::unique_ptr& kb, ob { throw obelisk::ParserException("fact could not be inserted into the database"); } + else + { + if (updateIsTrue) + { + fact.setIsTrue(true); + kb->updateIsTrue(fact); + } + } } } diff --git a/src/parser.h b/src/parser.h index 13ad7ea..573fbdc 100644 --- a/src/parser.h +++ b/src/parser.h @@ -17,12 +17,32 @@ namespace obelisk { + /** + * @brief The Parser is responsible for analyzing the language's key words + * and taking action based on its analysis. + * + */ class Parser { private: + /** + * @brief The Lexer object that the Parser is using to Parse a + * specific source file. + * + */ std::shared_ptr lexer_; + + /** + * @brief The current token that the lexer has retrieved. + * + */ int currentToken_ = 0; + /** + * @brief Set the current token. + * + * @param[in] currentToken The token should be ASCII character. + */ void setCurrentToken(int currentToken); std::unique_ptr logError(const char* str); @@ -64,7 +84,9 @@ 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); + void insertFact(std::unique_ptr& kb, + obelisk::Fact& fact, + bool updateIsTrue = false); void insertSuggestAction(std::unique_ptr& kb, obelisk::SuggestAction& suggestAction); void insertRule(std::unique_ptr& kb, obelisk::Rule& rule); From 09925b567cf007332680db379ca64dafaaa28731 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Mon, 20 Feb 2023 22:01:14 -0300 Subject: [PATCH 21/27] finish up comments --- src/ast/ast.h | 19 +++ src/ast/call_expression_ast.h | 46 +++++++ src/ast/error.h | 17 ++- src/ast/expression_ast.h | 16 ++- src/ast/function_ast.h | 35 ++++++ src/ast/number_expression_ast.h | 29 +++++ src/ast/prototype_ast.h | 47 ++++++- src/ast/variable_expression_ast.h | 32 ++++- src/lexer.h | 21 ++-- src/lib/include/obelisk.h | 6 +- src/lib/knowledge_base.h | 69 +++++----- src/lib/models/action.h | 9 +- src/lib/models/entity.h | 9 +- src/lib/models/error.h | 12 +- src/lib/models/fact.h | 24 ++-- src/lib/models/rule.h | 3 +- src/lib/models/suggest_action.h | 6 +- src/lib/models/verb.h | 3 +- src/main.h | 4 +- src/parser.h | 202 ++++++++++++++++++++++++++++-- 20 files changed, 492 insertions(+), 117 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 1002ace..94c0549 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -11,9 +11,28 @@ namespace obelisk { + /** + * @brief The LLVM context. + * + */ static std::unique_ptr TheContext; + + /** + * @brief The LLVM module. + * + */ static std::unique_ptr TheModule; + + /** + * @brief The LLVM IR builder. + * + */ static std::unique_ptr> Builder; + + /** + * @brief The LLVM named values. + * + */ static std::map NamedValues; } // namespace obelisk diff --git a/src/ast/call_expression_ast.h b/src/ast/call_expression_ast.h index 464a574..4913bd9 100644 --- a/src/ast/call_expression_ast.h +++ b/src/ast/call_expression_ast.h @@ -9,25 +9,71 @@ namespace obelisk { + /** + * @brief The call AST expression node used to call functions. + * + */ class CallExpressionAST : public ExpressionAST { private: + /** + * @brief The function being called. + * + */ std::string callee_; + + /** + * @brief The arguments passed to the function. + * + */ std::vector> args_; + /** + * @brief Get the callee. + * + * @return std::string Returns the name of the function being called. + */ std::string getCallee(); + + /** + * @brief Set the callee. + * + * @param[in] callee The name of the function. + */ void setCallee(std::string callee); + /** + * @brief Get the arguments being used by the function. + * + * @return std::vector> Returns an AST expression containing the args. + */ std::vector> getArgs(); + + /** + * @brief Set the arguments to be used by the function. + * + * @param[in] args The args to set. + */ void setArgs(std::vector> args); public: + /** + * @brief Construct a new CallExpressionAST object. + * + * @param[in] callee The function to call. + * @param[in] args The args to pass into the function. + */ CallExpressionAST(const std::string &callee, std::vector> args) : callee_(callee), args_(std::move(args)) { } + /** + * @brief Generate the calle IR code. + * + * @return llvm::Value* + */ llvm::Value *codegen() override; }; } // namespace obelisk diff --git a/src/ast/error.h b/src/ast/error.h index 1770f7f..dd990dc 100644 --- a/src/ast/error.h +++ b/src/ast/error.h @@ -7,8 +7,21 @@ namespace obelisk { - std::unique_ptr LogError(const char *Str); - llvm::Value *LogErrorV(const char *Str); + /** + * @brief Log an AST expression error. + * + * @param[in] str The error message. + * @return std::unique_ptr Returns the AST expression that caused the error. + */ + std::unique_ptr LogError(const char *str); + + /** + * @brief Log an AST value error. + * + * @param[in] str The error message. + * @return llvm::Value* Returns the AST value that caused the error. + */ + llvm::Value *LogErrorV(const char *str); } // namespace obelisk #endif diff --git a/src/ast/expression_ast.h b/src/ast/expression_ast.h index a77fff7..c1201a8 100644 --- a/src/ast/expression_ast.h +++ b/src/ast/expression_ast.h @@ -5,10 +5,24 @@ namespace obelisk { + /** + * @brief A generic AST expression which other expression will inherit from. + * + */ class ExpressionAST { public: - virtual ~ExpressionAST() = default; + /** + * @brief Destroy the ExpressionAST object. + * + */ + virtual ~ExpressionAST() = default; + + /** + * @brief Generate LLVM IR code based on the AST expression. + * + * @return llvm::Value* Returns the LLVM code value from the expression. + */ virtual llvm::Value *codegen() = 0; }; } // namespace obelisk diff --git a/src/ast/function_ast.h b/src/ast/function_ast.h index d9f433f..d4e28b1 100644 --- a/src/ast/function_ast.h +++ b/src/ast/function_ast.h @@ -8,22 +8,57 @@ namespace obelisk { + /** + * @brief A Funcion AST node. + * + */ class FunctionAST { private: + /** + * @brief The prototype of the function. + * + */ std::unique_ptr prototype_; + + /** + * @brief The body of the function. + * + */ std::unique_ptr body_; + /** + * @brief Get the prototype. + * + * @return std::unique_ptr Returns the prototype AST. + */ std::unique_ptr getPrototype(); + + /** + * @brief Set the prototype. + * + * @param[in] prototype Set the prototype. + */ void setPrototype(std::unique_ptr prototype); public: + /** + * @brief Construct a new FunctionAST object. + * + * @param[in] prototype The prototype of the function. + * @param[in] body The body of the function. + */ FunctionAST(std::unique_ptr prototype, std::unique_ptr body) : prototype_(std::move(prototype)), body_(std::move(body)) { } + /** + * @brief Generate LLVM IR code. + * + * @return llvm::Function* Returns the LLVM IR function code. + */ llvm::Function *codegen(); }; } // namespace obelisk diff --git a/src/ast/number_expression_ast.h b/src/ast/number_expression_ast.h index 874cb79..9ad9291 100644 --- a/src/ast/number_expression_ast.h +++ b/src/ast/number_expression_ast.h @@ -5,20 +5,49 @@ namespace obelisk { + /** + * @brief A number expression AST node. + * + */ class NumberExpressionAST : public ExpressionAST { private: + /** + * @brief The number. + * + */ double number_; + /** + * @brief Get the number. + * + * @return double Returns the number. + */ double getNumber(); + + /** + * @brief Set the number. + * + * @param[in] number The number. + */ void setNumber(double number); public: + /** + * @brief Construct a new NumberExpressionAST object. + * + * @param[in] number The number. + */ NumberExpressionAST(double number) : number_(number) { } + /** + * @brief Generate LLVM IR code for the number. + * + * @return llvm::Value* Returns the genrated IR code. + */ llvm::Value *codegen() override; }; } // namespace obelisk diff --git a/src/ast/prototype_ast.h b/src/ast/prototype_ast.h index f8f22a1..2df8225 100644 --- a/src/ast/prototype_ast.h +++ b/src/ast/prototype_ast.h @@ -8,30 +8,75 @@ namespace obelisk { + /** + * @brief The prototype AST node. + * + */ class PrototypeAST { private: + /** + * @brief The name of the prototype. + * + */ std::string name_; + /** + * @brief The arguments the protype accepts. + * + */ std::vector args_; + /** + * @brief Set the name of the prototype. + * + * @param[in] name The name. + */ void setName(const std::string& name); + + /** + * @brief Get the arguments the prototype accepts. + * + * @return std::vector Returns the arguments. + */ std::vector getArgs(); + + /** + * @brief Set the arguments the prototype accepts. + * + * @param[in] args The arguments. + */ void setArgs(std::vector args); public: + /** + * @brief Construct a new PrototypeAST object. + * + * @param[in] name The name of the prototype. + * @param[in] args The arguments the prototype accepts. + */ PrototypeAST(const std::string& name, std::vector args) : name_(name), args_(std::move(args)) { } + /** + * @brief Get the name of the prototype. + * + * @return const std::string& Returns the name of the prototype. + */ const std::string& getName() const { return name_; } + /** + * @brief Generate LLVM IR code for the prototype. + * + * @return llvm::Function* Returns IR code for the prototype. + */ llvm::Function* codegen(); }; -} //namespace obelisk +} // namespace obelisk #endif diff --git a/src/ast/variable_expression_ast.h b/src/ast/variable_expression_ast.h index 3b51f87..f6ba2b0 100644 --- a/src/ast/variable_expression_ast.h +++ b/src/ast/variable_expression_ast.h @@ -7,21 +7,51 @@ namespace obelisk { + /** + * @brief The variable expression AST node. + * + */ class VariableExpressionAST : public ExpressionAST { private: + /** + * @brief The name of the variable. + * + */ std::string name_; + + /** + * @brief Get the name of the variable. + * + * @return std::string Returns the name of the variable. + */ std::string getName(); + + /** + * @brief Set the name of the variable. + * + * @param[in] name The name of the variable. + */ void setName(const std::string name); public: + /** + * @brief Construct a new VariableExpressionAST object. + * + * @param[in] name The name of the variable. + */ VariableExpressionAST(const std::string &name) : name_(name) { } + /** + * @brief Generate the variable LLVM IR code. + * + * @return llvm::Value* Returns the generated IR code. + */ llvm::Value *codegen() override; }; -} //namespace obelisk +} // namespace obelisk #endif diff --git a/src/lexer.h b/src/lexer.h index 4562611..30741ca 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -56,22 +56,19 @@ namespace obelisk /** * @brief Comment the rest of the line. * - * @param[in] lastChar The char to check to see if it in the end of - * the line. + * @param[in] lastChar The char to check to see if it in the end of the line. */ void commentLine(int* lastChar); public: /** - * @brief These token represent recognized language keywords and - * language functionality. + * @brief These token represent recognized language keywords and language functionality. * */ enum Token { /** - * @brief End of file is returned when the source code is - * finished. + * @brief End of file is returned when the source code is finished. * */ kTokenEof = -1, @@ -82,8 +79,7 @@ namespace obelisk */ kTokenFact = -2, /** - * @brief A rule which is a relationship between a new fact a - * existing fact. + * @brief A rule which is a relationship between a new fact a existing fact. * */ kTokenRule = -3, @@ -137,16 +133,14 @@ namespace obelisk * @brief Gets the next token in the source code. * * @throws LexerException when an invalid token is found. - * @return int Returns a Token value or char if no known token was - * found. + * @return int Returns a Token value or char if no known token was found. */ int getToken(); /** * @brief Get the last identifier. * - * @return const std::string& Returns a string that contains the - * last found identifier. + * @return const std::string& Returns a string that contains the last found identifier. */ const std::string& getIdentifier(); @@ -194,8 +188,7 @@ namespace obelisk /** * @brief Return the exception's error message. * - * @return const char* Returns a string containing the error - * message. + * @return const char* Returns a string containing the error message. */ const char* what() const noexcept { diff --git a/src/lib/include/obelisk.h b/src/lib/include/obelisk.h index f379303..a73dc4c 100644 --- a/src/lib/include/obelisk.h +++ b/src/lib/include/obelisk.h @@ -1,15 +1,13 @@ #include /** - * @brief The obelisk namespace contains everything needed to compile obelisk. - * code. + * @brief The obelisk namespace contains everything needed to compile obelisk code. * */ namespace obelisk { /** - * @brief The obelisk library provides everything needed to consult the - * KnowledgeBase. + * @brief The obelisk library provides everything needed to consult the KnowledgeBase. * */ class Obelisk diff --git a/src/lib/knowledge_base.h b/src/lib/knowledge_base.h index ac463d2..b9a3368 100644 --- a/src/lib/knowledge_base.h +++ b/src/lib/knowledge_base.h @@ -18,8 +18,7 @@ namespace obelisk { /** - * @brief The KnowledgeBase class represents a collection of facts, rules, - * actions, and related language connectors. + * @brief The KnowledgeBase class represents a collection of facts, rules, actions, and related language connectors. * */ class KnowledgeBase @@ -46,8 +45,8 @@ namespace obelisk /** * @brief Enable foreign key functionality in the open database. * - * This must always be done when the connection is opened or it will - * not enforce the foreign key constraints. + * This must always be done when the connection is opened or it will not enforce the foreign key + * constraints. */ void enableForeignKeys(); @@ -62,8 +61,7 @@ namespace obelisk /** * @brief Construct a new KnowledgeBase object. * - * @param[in] filename The name of the file to save the knowledge - * base as. + * @param[in] filename The name of the file to save the knowledge base as. * @param[in] flags The flags to open the KnowledgeBase with. */ KnowledgeBase(const char* filename, int flags); @@ -71,8 +69,7 @@ namespace obelisk /** * @brief Construct a new KnowledgeBase object. * - * @param[in] filename The name of the file to save the knowledge - * base as. + * @param[in] filename The name of the file to save the knowledge base as. */ KnowledgeBase(const char* filename) : KnowledgeBase(filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) @@ -89,103 +86,96 @@ namespace obelisk /** * @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. + * @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. */ void addEntities(std::vector& entities); /** * @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. + * @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. + * @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 KnowledgeBase. * - * @param[in,out] facts The facts to add. If the insert is - * successful it will have a row ID, if not the ID will be 0. + * @param[in,out] facts The facts to add. If the insert is successful it will have a row ID, if not the ID + * will be 0. */ void addFacts(std::vector& facts); /** * @brief Add suggested actions to the KnowledgeBase. * - * @param[in,out] suggestActions The suggested actions to add. If - * the insert is successful it will have a row ID, if not the ID - * will be 0. + * @param[in,out] suggestActions The suggested actions to add. If the insert is successful it will have a + * row ID, if not the ID will be 0. */ void addSuggestActions(std::vector& suggestActions); /** * @brief Add rules to the KnowledgeBase. * - * @param[in,out] rules The rules to add. If the insert is successful it - * will have a row ID, if not the ID will be 0. + * @param[in,out] rules The rules to add. If the insert is successful it will have a row ID, if not the ID + * will be 0. */ void addRules(std::vector& rules); /** * @brief Get an Entity object based on the ID it contains. * - * @param[in,out] entity The Entity object should contain just the - * ID and the rest will be filled in. + * @param[in,out] entity The Entity object should contain just the ID and the rest will be filled in. */ void getEntity(obelisk::Entity& entity); /** * @brief Get a Verb object based on the ID it contains. * - * @param[in,out] verb The Verb object should contain just the ID - * and the rest will be filled in. + * @param[in,out] verb The Verb object should contain just the ID and the rest will be filled in. */ 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. + * @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. * - * @param[in,out] fact The Fact object should contain just the ID - * and the rest will be filled in. + * @param[in,out] fact The Fact object should contain just the ID and the rest will be filled in. */ void getFact(obelisk::Fact& fact); /** * @brief Get a SuggestAction based on the ID it contains. * - * @param[in,out] suggestAction The SuggestAction object should - * contain just the ID and the rest will be filled in. + * @param[in,out] suggestAction The SuggestAction object should contain just the ID and the rest will be + * filled in. */ void getSuggestAction(obelisk::SuggestAction& suggestAction); /** * @brief Get a Rule based on the ID it contains. * - * @param[in,out] rule The Rule object should contain just the ID - * and the rest will be filled in. + * @param[in,out] rule The Rule object should contain just the ID and the rest will be filled in. */ void getRule(obelisk::Rule& rule); /** - * @brief Check if a rule looks for this Fact, if so update its - * truth. + * @brief Check if a rule looks for this Fact, if so update its truth. * * @param[in,out] fact The Fact to check for existing rules. */ @@ -201,10 +191,8 @@ namespace obelisk /** * @brief Take a float and divide it into 2 floats. * - * This is useful to store doubles in SQLite since SQLite doesn't - * have a double type. - * Instead just store the 2 floats in the database. Then after - * selecting them combine them. + * This is useful to store doubles in SQLite since SQLite doesn't have a double type. Instead just store the + * 2 floats in the database. Then after selecting them combine them. * * @param[out] result1 The first float generated from the double. * @param[out] result2 The second float generated from the double. @@ -215,8 +203,7 @@ namespace obelisk /** * @brief Combines 2 separated floats back into a double. * - * This will recombine the separated floats from the getFloat - * method. + * This will recombine the separated floats from the getFloat method. * * @param[out] result The double generated from the combined floats. * @param[in] var1 The first float to combine. diff --git a/src/lib/models/action.h b/src/lib/models/action.h index 2d06c03..c45c536 100644 --- a/src/lib/models/action.h +++ b/src/lib/models/action.h @@ -8,8 +8,7 @@ namespace obelisk { /** - * @brief The Action model represents an action to take when a fact is true - * or false. + * @brief The Action model represents an action to take when a fact is true or false. * */ class Action @@ -108,16 +107,14 @@ namespace obelisk void setName(std::string name); /** - * @brief Select an Action from the datbase based on the object - * 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. + * @brief Insert an Action into the KnowledgeBase based on the object's fields. * * @param[in] dbConnection The database connection to use. */ diff --git a/src/lib/models/entity.h b/src/lib/models/entity.h index 3833f99..8f4ee1f 100644 --- a/src/lib/models/entity.h +++ b/src/lib/models/entity.h @@ -8,8 +8,7 @@ namespace obelisk { /** - * @brief The Entity model represents either a left or right side entity, - * typically used in facts and rules. + * @brief The Entity model represents either a left or right side entity, typically used in facts and rules. * */ class Entity @@ -108,16 +107,14 @@ namespace obelisk void setName(std::string name); /** - * @brief Select an Entity from the KnowledgeBase based on the object's - * name. + * @brief Select an Entity from the KnowledgeBase based on the object's name. * * @param[in] dbConnection The database connection to use. */ void selectByName(sqlite3* dbConnection); /** - * @brief Insert an Entity into the KnowledgeBase based on the object's - * fields. + * @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 86d0987..7e88a13 100644 --- a/src/lib/models/error.h +++ b/src/lib/models/error.h @@ -42,8 +42,7 @@ namespace obelisk /** * @brief Construct a new DatabaseException object. * - * @param[in] errorMessage The error message to describe the - * exception. + * @param[in] errorMessage The error message to describe the exception. */ DatabaseException(const std::string& errorMessage) : errorMessage_(errorMessage) @@ -72,8 +71,7 @@ namespace obelisk }; /** - * @brief Exception thrown if the string or blob size exceeds sqlite's - * limits. + * @brief Exception thrown if the string or blob size exceeds sqlite's limits. * */ class DatabaseSizeException : public obelisk::DatabaseException @@ -107,8 +105,7 @@ namespace obelisk }; /** - * @brief Exception thrown if there is not enough memory to perform the - * operation. + * @brief Exception thrown if there is not enough memory to perform the operation. * */ class DatabaseMemoryException : public obelisk::DatabaseException @@ -178,8 +175,7 @@ namespace obelisk /** * @brief Construct a new DatabaseConstraintException object. * - * @param[in] errorMessage The error message to send when the - * constraint is violated. + * @param[in] errorMessage The error message to send when the constraint is violated. */ DatabaseConstraintException(const std::string& errorMessage) { diff --git a/src/lib/models/fact.h b/src/lib/models/fact.h index cc16b06..1232224 100644 --- a/src/lib/models/fact.h +++ b/src/lib/models/fact.h @@ -10,8 +10,7 @@ namespace obelisk { /** - * @brief The Fact model represents truth in the releationship between two - * entities separated by a verb. + * @brief The Fact model represents truth in the releationship between two entities separated by a verb. * */ class Fact @@ -36,8 +35,7 @@ namespace obelisk obelisk::Entity rightEntity_; /** - * @brief The Verb that represents the relationship in the - * expression. + * @brief The Verb that represents the relationship in the expression. * */ obelisk::Verb verb_; @@ -79,10 +77,8 @@ 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] 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. */ @@ -99,10 +95,8 @@ 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] 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. */ @@ -198,8 +192,7 @@ namespace obelisk void setIsTrue(bool isTrue); /** - * @brief Select the Fact from the KnowledgeBase by IDs of the - * sub-objects. + * @brief Select the Fact from the KnowledgeBase by IDs of the sub-objects. * * @param[in] dbConnection The database connection to use. */ @@ -213,8 +206,7 @@ namespace obelisk void insert(sqlite3* dbConnection); /** - * @brief Update whether or not the fact is true in the - * KnowledgeBase. + * @brief Update whether or not the fact is true in the KnowledgeBase. * * @param[in] dbConnection The database connection. */ diff --git a/src/lib/models/rule.h b/src/lib/models/rule.h index 02f06fd..04d3efb 100644 --- a/src/lib/models/rule.h +++ b/src/lib/models/rule.h @@ -134,8 +134,7 @@ namespace obelisk void setReason(obelisk::Fact reason); /** - * @brief Select the Rule from the KnowledgeBase by IDs of the - * sub-objects. + * @brief Select the Rule from the KnowledgeBase by IDs of the sub-objects. * * @param[in] dbConnection The database connection to use. */ diff --git a/src/lib/models/suggest_action.h b/src/lib/models/suggest_action.h index e5e18b7..f6854e4 100644 --- a/src/lib/models/suggest_action.h +++ b/src/lib/models/suggest_action.h @@ -9,8 +9,7 @@ namespace obelisk { /** - * @brief The SuggestAction model representas the actions to take depending - * on if the Fact is true or false. + * @brief The SuggestAction model representas the actions to take depending on if the Fact is true or false. * */ class SuggestAction @@ -161,8 +160,7 @@ namespace obelisk void setFalseAction(obelisk::Action falseAction); /** - * @brief Select the SuggestAction from the KnowledgeBase by IDs of the - * sub-objects. + * @brief Select the SuggestAction from the KnowledgeBase by IDs of the sub-objects. * * @param[in] dbConnection The database connection to use. */ diff --git a/src/lib/models/verb.h b/src/lib/models/verb.h index 150e86f..2edc546 100644 --- a/src/lib/models/verb.h +++ b/src/lib/models/verb.h @@ -8,8 +8,7 @@ namespace obelisk { /** - * @brief The Verb model represents a verb which is used to connnect - * entities. + * @brief The Verb model represents a verb which is used to connnect entities. * */ class Verb diff --git a/src/main.h b/src/main.h index e3abfcb..3d79355 100644 --- a/src/main.h +++ b/src/main.h @@ -1,8 +1,7 @@ #include /** - * @brief The obelisk namespace contains everything needed to compile obelisk. - * code. + * @brief The obelisk namespace contains everything needed to compile obelisk code. * */ namespace obelisk @@ -38,6 +37,7 @@ Options: /** * @brief This is the main loop for obelisk. + * * This loop handles lexing and parsing of obelisk source code. * * @return int Returns EXIT_SUCCESS or EXIT_FAILURE. diff --git a/src/parser.h b/src/parser.h index 573fbdc..436fa13 100644 --- a/src/parser.h +++ b/src/parser.h @@ -18,16 +18,14 @@ namespace obelisk { /** - * @brief The Parser is responsible for analyzing the language's key words - * and taking action based on its analysis. + * @brief The Parser is responsible for analyzing the language's key words and taking action based on its analysis. * */ class Parser { private: /** - * @brief The Lexer object that the Parser is using to Parse a - * specific source file. + * @brief The Lexer object that the Parser is using to Parse a specific source file. * */ std::shared_ptr lexer_; @@ -45,69 +43,259 @@ namespace obelisk */ void setCurrentToken(int currentToken); + /** + * @brief Log errors from the LLVM parsing. + * + * @param[in] str The error message. + * @return std::unique_ptr Returns the AST expression that caused the error. + */ std::unique_ptr logError(const char* str); + + /** + * @brief Log errors from the LLVM parsing involving the prototypes. + * + * @param[in] str The error message. + * @return std::unique_ptr Returns the AST for the prototype. + */ std::unique_ptr logErrorPrototype(const char* str); + /** + * @brief The AST expression parser. + * + * @return std::unique_ptr Returns the parsed AST expression. + */ std::unique_ptr parseExpression(); + + /** + * @brief The AST number expression parser. + * + * @return std::unique_ptr Returns the parsed AST expression. + */ std::unique_ptr parseNumberExpression(); + + /** + * @brief The AST parenthesis expression parser. + * + * @return std::unique_ptr Returns the parsed AST expression. + */ std::unique_ptr parseParenthesisExpression(); + + /** + * @brief The AST identifier expression parser. + * + * @return std::unique_ptr Returns the parsed AST expression. + */ std::unique_ptr parseIdentifierExpression(); + + /** + * @brief The AST primary expression parser. + * + * @return std::unique_ptr Returns the parsed AST expression. + */ std::unique_ptr parsePrimary(); + + /** + * @brief The AST prototype parser. + * + * @return std::unique_ptr Returns the parsed AST prototype expression. + */ std::unique_ptr parsePrototype(); + + /** + * @brief The AST definition parser. + * + * @return std::unique_ptr Returns the parsed AST definition expression. + */ std::unique_ptr parseDefinition(); + + /** + * @brief The AST top level expression parser. + * + * @return std::unique_ptr Returns the parsed AST top level expression. + */ std::unique_ptr parseTopLevelExpression(); + + /** + * @brief The AST external definition parser. + * + * @return std::unique_ptr Returns the parsed AST external definition. + */ std::unique_ptr parseExtern(); + + /** + * @brief Parse a SuggestAction. + * + * @param[out] suggestAction The parsed SuggestAction. + */ void parseAction(obelisk::SuggestAction& suggestAction); + + /** + * @brief Parse a Rule. + * + * @param[out] rule The parsed Rule. + */ void parseRule(obelisk::Rule& rule); + + /** + * @brief Parse Facts. + * + * @param[out] facts The parsed Facts. + */ void parseFact(std::vector& facts); public: + /** + * @brief Construct a new Parser object. + * + * @param[in] lexer The lexer the parser uses to retrieve parts of the language. + */ Parser(std::shared_ptr lexer) : lexer_(lexer) { } + /** + * @brief Get the Lexer. + * + * @return std::shared_ptr Returns the current Lexer in use by the Parser. + */ std::shared_ptr getLexer(); + + /** + * @brief Set the Lexer to use during the parsing phase. + * + * @param[in] lexer The Lexer. + */ void setLexer(std::shared_ptr lexer); + /** + * @brief Gets the current token held inside the Lexer. + * + * @return int Returns the current token. + */ int getCurrentToken(); + /** + * @brief Instructs the Lexer to retrieve a new token. + * + * @return int Returns the next token. + */ int getNextToken(); - void handleDefinition(); - void handleExtern(); - void handleTopLevelExpression(); + /** + * @brief Parse the SuggestAction and then insert it into the KnowledgeBase. + * + * @param[in] kb The KnowledgeBase to insert the SuggestAction into. + */ void handleAction(std::unique_ptr& kb); + + /** + * @brief Parse the Rule and then insert it into the KnowledgeBase. + * + * @param[in] kb The KnowledgeBase to insert the Rule into. + */ void handleRule(std::unique_ptr& kb); + + /** + * @brief Parse the Fact and then insert it into the KnowledgeBase. + * + * @param[in] kb The KnowledgeBase to insert the Fact into. + */ void handleFact(std::unique_ptr& kb); + /** + * @brief Helper used to insert an Entity into the KnowledgeBase. + * + * @param[in] kb The KnowledgeBase to use. + * @param[in,out] entity The Entity to insert. It will contain the ID of the Entity after inserting it. + */ void insertEntity(std::unique_ptr& kb, obelisk::Entity& entity); + + /** + * @brief Helper used to insert a Verb into the KnowledgeBase. + * + * @param[in] kb The KnowledegeBase to use. + * @param[in,out] verb The Verb to insert. It will contain the ID of the Verb after inserting it. + */ void insertVerb(std::unique_ptr& kb, obelisk::Verb& verb); + + /** + * @brief Helper used to insert an Action into the KnowledgeBase. + * + * @param[in] kb The KnowledgeBase to use. + * @param[in,out] action The Action to insert. It will contain the ID of the Action after inserting it. + */ void insertAction(std::unique_ptr& kb, obelisk::Action& action); + + /** + * @brief Helper used to insert a Fact into the KnowledgeBase. + * + * @param[in] kb The KnowledgeBase to use. + * @param[in,out] fact The Fact to insert. It will contain the ID of the Fact after inserting it. + * @param[in] updateIsTrue If true, it will update the value of is_true in the KnowledgeBase if the Fact + * already exists. + */ void insertFact(std::unique_ptr& kb, obelisk::Fact& fact, bool updateIsTrue = false); + + /** + * @brief Helper used to insert a SuggestAction into the KnowledgeBase. + * + * @param[in] kb The KnowledgeBase to use. + * @param[in,out] suggestAction The SuggestAction to insert. It will contain the ID of the SuggestAction + * after inserting it. + */ void insertSuggestAction(std::unique_ptr& kb, obelisk::SuggestAction& suggestAction); + + /** + * @brief Helper usedto insert a Rule into the KnowledgeBase. + * + * @param[in] kb The KnowledgeBase to use. + * @param[in,out] rule The Rule to insert. It will contain the ID of the Rule after inserting it. + */ void insertRule(std::unique_ptr& kb, obelisk::Rule& rule); }; + /** + * @brief The exceptions thrown by the Parser. + * + */ class ParserException : public std::exception { private: + /** + * @brief The error message. + * + */ const std::string errorMessage_; public: + /** + * @brief Construct a new ParserException object. + * + */ ParserException() : errorMessage_("an unknown error ocurred") { } + /** + * @brief Construct a new ParserException object. + * + * @param[in] errorMessage The error message. + */ ParserException(const std::string& errorMessage) : errorMessage_(errorMessage) { } + /** + * @brief Return the error message as a C style string. + * + * @return const char* Returns the error message. + */ const char* what() const noexcept { return errorMessage_.c_str(); From adc5042c6c57333338f26adab1fd2425dbb9a703 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Mon, 20 Feb 2023 22:05:57 -0300 Subject: [PATCH 22/27] format to 80 columns for terminal display --- .clang-format | 4 +- src/ast/call_expression_ast.h | 9 ++- src/ast/error.h | 3 +- src/ast/expression_ast.h | 3 +- src/ast/function_ast.cpp | 3 +- src/ast/function_ast.h | 3 +- src/ast/prototype_ast.cpp | 13 +++- src/ast/prototype_ast.h | 3 +- src/lexer.cpp | 3 +- src/lexer.h | 21 +++-- src/lib/include/obelisk.h | 6 +- src/lib/knowledge_base.cpp | 32 ++++++-- src/lib/knowledge_base.h | 74 ++++++++++-------- src/lib/models/action.cpp | 18 ++++- src/lib/models/action.h | 9 ++- src/lib/models/entity.cpp | 18 ++++- src/lib/models/entity.h | 9 ++- src/lib/models/error.h | 18 +++-- src/lib/models/fact.cpp | 15 +++- src/lib/models/fact.h | 29 ++++--- src/lib/models/rule.cpp | 21 +++-- src/lib/models/rule.h | 7 +- src/lib/models/suggest_action.cpp | 3 +- src/lib/models/suggest_action.h | 15 +++- src/lib/models/verb.cpp | 18 ++++- src/lib/models/verb.h | 3 +- src/main.cpp | 21 +++-- src/main.h | 6 +- src/parser.cpp | 124 ++++++++++++++++++++---------- src/parser.h | 95 +++++++++++++++-------- 30 files changed, 412 insertions(+), 194 deletions(-) diff --git a/.clang-format b/.clang-format index c46124e..7233005 100644 --- a/.clang-format +++ b/.clang-format @@ -63,7 +63,7 @@ BreakBeforeTernaryOperators: true BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon BreakStringLiterals: false -ColumnLimit: 120 +ColumnLimit: 80 CommentPragmas: "^ IWYU pragma:" CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true @@ -167,7 +167,7 @@ RawStringFormats: - PROTO CanonicalDelimiter: pb ReferenceAlignment: Left -ReflowComments: false +ReflowComments: true RemoveBracesLLVM: false SeparateDefinitionBlocks: Always ShortNamespaceLines: 0 diff --git a/src/ast/call_expression_ast.h b/src/ast/call_expression_ast.h index 4913bd9..55c5800 100644 --- a/src/ast/call_expression_ast.h +++ b/src/ast/call_expression_ast.h @@ -31,7 +31,8 @@ namespace obelisk /** * @brief Get the callee. * - * @return std::string Returns the name of the function being called. + * @return std::string Returns the name of the function being + * called. */ std::string getCallee(); @@ -45,7 +46,8 @@ namespace obelisk /** * @brief Get the arguments being used by the function. * - * @return std::vector> Returns an AST expression containing the args. + * @return std::vector> Returns an + * AST expression containing the args. */ std::vector> getArgs(); @@ -63,7 +65,8 @@ namespace obelisk * @param[in] callee The function to call. * @param[in] args The args to pass into the function. */ - CallExpressionAST(const std::string &callee, std::vector> args) : + CallExpressionAST(const std::string &callee, + std::vector> args) : callee_(callee), args_(std::move(args)) { diff --git a/src/ast/error.h b/src/ast/error.h index dd990dc..78d1770 100644 --- a/src/ast/error.h +++ b/src/ast/error.h @@ -11,7 +11,8 @@ namespace obelisk * @brief Log an AST expression error. * * @param[in] str The error message. - * @return std::unique_ptr Returns the AST expression that caused the error. + * @return std::unique_ptr Returns the AST expression that + * caused the error. */ std::unique_ptr LogError(const char *str); diff --git a/src/ast/expression_ast.h b/src/ast/expression_ast.h index c1201a8..d80c378 100644 --- a/src/ast/expression_ast.h +++ b/src/ast/expression_ast.h @@ -21,7 +21,8 @@ namespace obelisk /** * @brief Generate LLVM IR code based on the AST expression. * - * @return llvm::Value* Returns the LLVM code value from the expression. + * @return llvm::Value* Returns the LLVM code value from the + * expression. */ virtual llvm::Value *codegen() = 0; }; diff --git a/src/ast/function_ast.cpp b/src/ast/function_ast.cpp index 021e9bb..1bdad9d 100644 --- a/src/ast/function_ast.cpp +++ b/src/ast/function_ast.cpp @@ -17,7 +17,8 @@ llvm::Function *obelisk::FunctionAST::codegen() return nullptr; } - llvm::BasicBlock *bB = llvm::BasicBlock::Create(*TheContext, "entry", theFunction); + llvm::BasicBlock *bB + = llvm::BasicBlock::Create(*TheContext, "entry", theFunction); Builder->SetInsertPoint(bB); NamedValues.clear(); diff --git a/src/ast/function_ast.h b/src/ast/function_ast.h index d4e28b1..8f3e0ac 100644 --- a/src/ast/function_ast.h +++ b/src/ast/function_ast.h @@ -48,7 +48,8 @@ namespace obelisk * @param[in] prototype The prototype of the function. * @param[in] body The body of the function. */ - FunctionAST(std::unique_ptr prototype, std::unique_ptr body) : + FunctionAST(std::unique_ptr prototype, + std::unique_ptr body) : prototype_(std::move(prototype)), body_(std::move(body)) { diff --git a/src/ast/prototype_ast.cpp b/src/ast/prototype_ast.cpp index 72e096a..f7caba4 100644 --- a/src/ast/prototype_ast.cpp +++ b/src/ast/prototype_ast.cpp @@ -3,10 +3,17 @@ llvm::Function *obelisk::PrototypeAST::codegen() { - std::vector doubles(args_.size(), llvm::Type::getDoubleTy(*TheContext)); - llvm::FunctionType *FT = llvm::FunctionType::get(llvm::Type::getDoubleTy(*TheContext), doubles, false); + std::vector doubles(args_.size(), + llvm::Type::getDoubleTy(*TheContext)); + llvm::FunctionType *FT + = llvm::FunctionType::get(llvm::Type::getDoubleTy(*TheContext), + doubles, + false); - llvm::Function *F = llvm::Function::Create(FT, llvm::Function::ExternalLinkage, name_, obelisk::TheModule.get()); + llvm::Function *F = llvm::Function::Create(FT, + llvm::Function::ExternalLinkage, + name_, + obelisk::TheModule.get()); unsigned idx = 0; for (auto &arg : F->args()) diff --git a/src/ast/prototype_ast.h b/src/ast/prototype_ast.h index 2df8225..ec1e7bc 100644 --- a/src/ast/prototype_ast.h +++ b/src/ast/prototype_ast.h @@ -54,7 +54,8 @@ namespace obelisk * @param[in] name The name of the prototype. * @param[in] args The arguments the prototype accepts. */ - PrototypeAST(const std::string& name, std::vector args) : + PrototypeAST(const std::string& name, + std::vector args) : name_(name), args_(std::move(args)) { diff --git a/src/lexer.cpp b/src/lexer.cpp index c941103..91eb20d 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -7,7 +7,8 @@ obelisk::Lexer::Lexer(const std::string& sourceFile) fileStream_.open(sourceFile, std::ifstream::in); if (!fileStream_) { - throw obelisk::LexerException("could not open source file " + sourceFile); + throw obelisk::LexerException( + "could not open source file " + sourceFile); } } diff --git a/src/lexer.h b/src/lexer.h index 30741ca..4562611 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -56,19 +56,22 @@ namespace obelisk /** * @brief Comment the rest of the line. * - * @param[in] lastChar The char to check to see if it in the end of the line. + * @param[in] lastChar The char to check to see if it in the end of + * the line. */ void commentLine(int* lastChar); public: /** - * @brief These token represent recognized language keywords and language functionality. + * @brief These token represent recognized language keywords and + * language functionality. * */ enum Token { /** - * @brief End of file is returned when the source code is finished. + * @brief End of file is returned when the source code is + * finished. * */ kTokenEof = -1, @@ -79,7 +82,8 @@ namespace obelisk */ kTokenFact = -2, /** - * @brief A rule which is a relationship between a new fact a existing fact. + * @brief A rule which is a relationship between a new fact a + * existing fact. * */ kTokenRule = -3, @@ -133,14 +137,16 @@ namespace obelisk * @brief Gets the next token in the source code. * * @throws LexerException when an invalid token is found. - * @return int Returns a Token value or char if no known token was found. + * @return int Returns a Token value or char if no known token was + * found. */ int getToken(); /** * @brief Get the last identifier. * - * @return const std::string& Returns a string that contains the last found identifier. + * @return const std::string& Returns a string that contains the + * last found identifier. */ const std::string& getIdentifier(); @@ -188,7 +194,8 @@ namespace obelisk /** * @brief Return the exception's error message. * - * @return const char* Returns a string containing the error message. + * @return const char* Returns a string containing the error + * message. */ const char* what() const noexcept { diff --git a/src/lib/include/obelisk.h b/src/lib/include/obelisk.h index a73dc4c..b06eb8b 100644 --- a/src/lib/include/obelisk.h +++ b/src/lib/include/obelisk.h @@ -1,13 +1,15 @@ #include /** - * @brief The obelisk namespace contains everything needed to compile obelisk code. + * @brief The obelisk namespace contains everything needed to compile obelisk + * code. * */ namespace obelisk { /** - * @brief The obelisk library provides everything needed to consult the KnowledgeBase. + * @brief The obelisk library provides everything needed to consult the + * KnowledgeBase. * */ class Obelisk diff --git a/src/lib/knowledge_base.cpp b/src/lib/knowledge_base.cpp index f5f4170..52fa521 100644 --- a/src/lib/knowledge_base.cpp +++ b/src/lib/knowledge_base.cpp @@ -44,7 +44,11 @@ obelisk::KnowledgeBase::~KnowledgeBase() void obelisk::KnowledgeBase::enableForeignKeys() { char* errmsg; - int result = sqlite3_exec(dbConnection_, "PRAGMA foreign_keys = ON;", NULL, NULL, &errmsg); + int result = sqlite3_exec(dbConnection_, + "PRAGMA foreign_keys = ON;", + NULL, + NULL, + &errmsg); if (result != SQLITE_OK) { if (errmsg) @@ -86,7 +90,9 @@ void obelisk::KnowledgeBase::addEntities(std::vector& entities) catch (obelisk::DatabaseConstraintException& exception) { // ignore unique constraint error - if (std::strcmp(exception.what(), "UNIQUE constraint failed: entity.name") != 0) + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: entity.name") + != 0) { throw; } @@ -105,7 +111,9 @@ void obelisk::KnowledgeBase::addVerbs(std::vector& verbs) catch (obelisk::DatabaseConstraintException& exception) { // ignore unique constraint error - if (std::strcmp(exception.what(), "UNIQUE constraint failed: verb.name") != 0) + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: verb.name") + != 0) { throw; } @@ -124,7 +132,9 @@ void obelisk::KnowledgeBase::addActions(std::vector& actions) catch (obelisk::DatabaseConstraintException& exception) { // ignore unique constraint error - if (std::strcmp(exception.what(), "UNIQUE constraint failed: action.name") != 0) + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: action.name") + != 0) { throw; } @@ -153,7 +163,8 @@ void obelisk::KnowledgeBase::addFacts(std::vector& facts) } } -void obelisk::KnowledgeBase::addSuggestActions(std::vector& suggestActions) +void obelisk::KnowledgeBase::addSuggestActions( + std::vector& suggestActions) { for (auto& suggestAction : suggestActions) { @@ -185,7 +196,9 @@ void obelisk::KnowledgeBase::addRules(std::vector& rules) catch (obelisk::DatabaseConstraintException& exception) { // ignore unique constraint error - if (std::strcmp(exception.what(), "UNIQUE constraint failed: rule.fact, rule.reason") != 0) + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: rule.fact, rule.reason") + != 0) { throw; } @@ -213,7 +226,8 @@ void obelisk::KnowledgeBase::getFact(obelisk::Fact& fact) fact.selectById(dbConnection_); } -void obelisk::KnowledgeBase::getSuggestAction(obelisk::SuggestAction& suggestAction) +void obelisk::KnowledgeBase::getSuggestAction( + obelisk::SuggestAction& suggestAction) { suggestAction.selectById(dbConnection_); } @@ -245,7 +259,9 @@ void obelisk::KnowledgeBase::updateIsTrue(obelisk::Fact& fact) fact.updateIsTrue(dbConnection_); } -void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) +void obelisk::KnowledgeBase::getFloat(float& result1, + float& result2, + double var) { result1 = (float) var; result2 = (float) (var - (double) result1); diff --git a/src/lib/knowledge_base.h b/src/lib/knowledge_base.h index b9a3368..4fe6b6a 100644 --- a/src/lib/knowledge_base.h +++ b/src/lib/knowledge_base.h @@ -18,7 +18,8 @@ namespace obelisk { /** - * @brief The KnowledgeBase class represents a collection of facts, rules, actions, and related language connectors. + * @brief The KnowledgeBase class represents a collection of facts, rules, + * actions, and related language connectors. * */ class KnowledgeBase @@ -45,8 +46,8 @@ namespace obelisk /** * @brief Enable foreign key functionality in the open database. * - * This must always be done when the connection is opened or it will not enforce the foreign key - * constraints. + * This must always be done when the connection is opened or it will + * not enforce the foreign key constraints. */ void enableForeignKeys(); @@ -61,7 +62,8 @@ namespace obelisk /** * @brief Construct a new KnowledgeBase object. * - * @param[in] filename The name of the file to save the knowledge base as. + * @param[in] filename The name of the file to save the knowledge + * base as. * @param[in] flags The flags to open the KnowledgeBase with. */ KnowledgeBase(const char* filename, int flags); @@ -69,10 +71,12 @@ namespace obelisk /** * @brief Construct a new KnowledgeBase object. * - * @param[in] filename The name of the file to save the knowledge base as. + * @param[in] filename The name of the file to save the knowledge + * base as. */ KnowledgeBase(const char* filename) : - KnowledgeBase(filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) + KnowledgeBase(filename, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) { } @@ -86,96 +90,104 @@ namespace obelisk /** * @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. + * @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. */ void addEntities(std::vector& entities); /** * @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. + * @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. + * @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 KnowledgeBase. * - * @param[in,out] facts The facts to add. If the insert is successful it will have a row ID, if not the ID - * will be 0. + * @param[in,out] facts The facts to add. If the insert is + * successful it will have a row ID, if not the ID will be 0. */ void addFacts(std::vector& facts); /** * @brief Add suggested actions to the KnowledgeBase. * - * @param[in,out] suggestActions The suggested actions to add. If the insert is successful it will have a - * row ID, if not the ID will be 0. + * @param[in,out] suggestActions The suggested actions to add. If + * the insert is successful it will have a row ID, if not the ID + * will be 0. */ - void addSuggestActions(std::vector& suggestActions); + void addSuggestActions( + std::vector& suggestActions); /** * @brief Add rules to the KnowledgeBase. * - * @param[in,out] rules The rules to add. If the insert is successful it will have a row ID, if not the ID - * will be 0. + * @param[in,out] rules The rules to add. If the insert is + * successful it will have a row ID, if not the ID will be 0. */ void addRules(std::vector& rules); /** * @brief Get an Entity object based on the ID it contains. * - * @param[in,out] entity The Entity object should contain just the ID and the rest will be filled in. + * @param[in,out] entity The Entity object should contain just the + * ID and the rest will be filled in. */ void getEntity(obelisk::Entity& entity); /** * @brief Get a Verb object based on the ID it contains. * - * @param[in,out] verb The Verb object should contain just the ID and the rest will be filled in. + * @param[in,out] verb The Verb object should contain just the ID + * and the rest will be filled in. */ 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. + * @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. * - * @param[in,out] fact The Fact object should contain just the ID and the rest will be filled in. + * @param[in,out] fact The Fact object should contain just the ID + * and the rest will be filled in. */ void getFact(obelisk::Fact& fact); /** * @brief Get a SuggestAction based on the ID it contains. * - * @param[in,out] suggestAction The SuggestAction object should contain just the ID and the rest will be - * filled in. + * @param[in,out] suggestAction The SuggestAction object should + * contain just the ID and the rest will be filled in. */ void getSuggestAction(obelisk::SuggestAction& suggestAction); /** * @brief Get a Rule based on the ID it contains. * - * @param[in,out] rule The Rule object should contain just the ID and the rest will be filled in. + * @param[in,out] rule The Rule object should contain just the ID + * and the rest will be filled in. */ void getRule(obelisk::Rule& rule); /** - * @brief Check if a rule looks for this Fact, if so update its truth. + * @brief Check if a rule looks for this Fact, if so update its + * truth. * * @param[in,out] fact The Fact to check for existing rules. */ @@ -191,8 +203,9 @@ namespace obelisk /** * @brief Take a float and divide it into 2 floats. * - * This is useful to store doubles in SQLite since SQLite doesn't have a double type. Instead just store the - * 2 floats in the database. Then after selecting them combine them. + * This is useful to store doubles in SQLite since SQLite doesn't + * have a double type. Instead just store the 2 floats in the + * database. Then after selecting them combine them. * * @param[out] result1 The first float generated from the double. * @param[out] result2 The second float generated from the double. @@ -203,7 +216,8 @@ namespace obelisk /** * @brief Combines 2 separated floats back into a double. * - * This will recombine the separated floats from the getFloat method. + * This will recombine the separated floats from the getFloat + * method. * * @param[out] result The double generated from the combined floats. * @param[in] var1 The first float to combine. diff --git a/src/lib/models/action.cpp b/src/lib/models/action.cpp index d6d3e10..8f8c880 100644 --- a/src/lib/models/action.cpp +++ b/src/lib/models/action.cpp @@ -21,7 +21,11 @@ void obelisk::Action::selectByName(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; - auto result = sqlite3_prepare_v2(dbConnection, "SELECT id, name FROM action WHERE name=?", -1, &ppStmt, nullptr); + auto result = sqlite3_prepare_v2(dbConnection, + "SELECT id, name FROM action WHERE name=?", + -1, + &ppStmt, + nullptr); if (result != SQLITE_OK) { @@ -85,13 +89,18 @@ void obelisk::Action::insert(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; - auto result = sqlite3_prepare_v2(dbConnection, "INSERT INTO action (name) VALUES (?)", -1, &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); + result + = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_TRANSIENT); switch (result) { case SQLITE_OK : @@ -118,7 +127,8 @@ void obelisk::Action::insert(sqlite3* dbConnection) sqlite3_set_last_insert_rowid(dbConnection, 0); break; case SQLITE_CONSTRAINT : - throw obelisk::DatabaseConstraintException(sqlite3_errmsg(dbConnection)); + throw obelisk::DatabaseConstraintException( + sqlite3_errmsg(dbConnection)); case SQLITE_BUSY : throw obelisk::DatabaseBusyException(); break; diff --git a/src/lib/models/action.h b/src/lib/models/action.h index c45c536..2d06c03 100644 --- a/src/lib/models/action.h +++ b/src/lib/models/action.h @@ -8,7 +8,8 @@ namespace obelisk { /** - * @brief The Action model represents an action to take when a fact is true or false. + * @brief The Action model represents an action to take when a fact is true + * or false. * */ class Action @@ -107,14 +108,16 @@ namespace obelisk void setName(std::string name); /** - * @brief Select an Action from the datbase based on the object 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. + * @brief Insert an Action into the KnowledgeBase based on the + * object's fields. * * @param[in] dbConnection The database connection to use. */ diff --git a/src/lib/models/entity.cpp b/src/lib/models/entity.cpp index 8d9250f..d90da0a 100644 --- a/src/lib/models/entity.cpp +++ b/src/lib/models/entity.cpp @@ -21,7 +21,11 @@ void obelisk::Entity::selectByName(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; - auto result = sqlite3_prepare_v2(dbConnection, "SELECT id, name FROM entity WHERE name=?", -1, &ppStmt, nullptr); + auto result = sqlite3_prepare_v2(dbConnection, + "SELECT id, name FROM entity WHERE name=?", + -1, + &ppStmt, + nullptr); if (result != SQLITE_OK) { @@ -85,13 +89,18 @@ void obelisk::Entity::insert(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; - auto result = sqlite3_prepare_v2(dbConnection, "INSERT INTO entity (name) VALUES (?)", -1, &ppStmt, nullptr); + auto result = sqlite3_prepare_v2(dbConnection, + "INSERT INTO entity (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); + result + = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_TRANSIENT); switch (result) { case SQLITE_OK : @@ -118,7 +127,8 @@ void obelisk::Entity::insert(sqlite3* dbConnection) sqlite3_set_last_insert_rowid(dbConnection, 0); break; case SQLITE_CONSTRAINT : - throw obelisk::DatabaseConstraintException(sqlite3_errmsg(dbConnection)); + throw obelisk::DatabaseConstraintException( + sqlite3_errmsg(dbConnection)); case SQLITE_BUSY : throw obelisk::DatabaseBusyException(); break; diff --git a/src/lib/models/entity.h b/src/lib/models/entity.h index 8f4ee1f..0241575 100644 --- a/src/lib/models/entity.h +++ b/src/lib/models/entity.h @@ -8,7 +8,8 @@ namespace obelisk { /** - * @brief The Entity model represents either a left or right side entity, typically used in facts and rules. + * @brief The Entity model represents either a left or right side entity, + * typically used in facts and rules. * */ class Entity @@ -107,14 +108,16 @@ namespace obelisk void setName(std::string name); /** - * @brief Select an Entity from the KnowledgeBase based on the object's name. + * @brief Select an Entity from the KnowledgeBase based on the + * object's name. * * @param[in] dbConnection The database connection to use. */ void selectByName(sqlite3* dbConnection); /** - * @brief Insert an Entity into the KnowledgeBase based on the object's fields. + * @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 7e88a13..ba28bd7 100644 --- a/src/lib/models/error.h +++ b/src/lib/models/error.h @@ -35,14 +35,16 @@ namespace obelisk * @param[in] errorCode The error code that came from sqlite. */ DatabaseException(const int errorCode) : - errorMessage_("database error " + std::to_string(errorCode) + " ocurred") + errorMessage_( + "database error " + std::to_string(errorCode) + " ocurred") { } /** * @brief Construct a new DatabaseException object. * - * @param[in] errorMessage The error message to describe the exception. + * @param[in] errorMessage The error message to describe the + * exception. */ DatabaseException(const std::string& errorMessage) : errorMessage_(errorMessage) @@ -71,7 +73,8 @@ namespace obelisk }; /** - * @brief Exception thrown if the string or blob size exceeds sqlite's limits. + * @brief Exception thrown if the string or blob size exceeds sqlite's + * limits. * */ class DatabaseSizeException : public obelisk::DatabaseException @@ -105,7 +108,8 @@ namespace obelisk }; /** - * @brief Exception thrown if there is not enough memory to perform the operation. + * @brief Exception thrown if there is not enough memory to perform the + * operation. * */ class DatabaseMemoryException : public obelisk::DatabaseException @@ -134,7 +138,8 @@ namespace obelisk */ DatabaseBusyException() { - setErrorMessage("database was busy and operation was not performed"); + setErrorMessage( + "database was busy and operation was not performed"); } }; @@ -175,7 +180,8 @@ namespace obelisk /** * @brief Construct a new DatabaseConstraintException object. * - * @param[in] errorMessage The error message to send when the constraint is violated. + * @param[in] errorMessage The error message to send when the + * constraint is violated. */ DatabaseConstraintException(const std::string& errorMessage) { diff --git a/src/lib/models/fact.cpp b/src/lib/models/fact.cpp index eafe955..ed6dad5 100644 --- a/src/lib/models/fact.cpp +++ b/src/lib/models/fact.cpp @@ -36,7 +36,8 @@ void obelisk::Fact::selectById(sqlite3* dbConnection) } else { - query = "SELECT id, left_entity, right_entity, verb, is_true FROM fact WHERE (id=?)"; + query + = "SELECT id, left_entity, right_entity, verb, is_true FROM fact WHERE (id=?)"; } auto result = sqlite3_prepare_v2(dbConnection, query, -1, &ppStmt, nullptr); if (result != SQLITE_OK) @@ -259,7 +260,8 @@ void obelisk::Fact::insert(sqlite3* dbConnection) sqlite3_set_last_insert_rowid(dbConnection, 0); break; case SQLITE_CONSTRAINT : - throw obelisk::DatabaseConstraintException(sqlite3_errmsg(dbConnection)); + throw obelisk::DatabaseConstraintException( + sqlite3_errmsg(dbConnection)); case SQLITE_BUSY : throw obelisk::DatabaseBusyException(); break; @@ -287,7 +289,11 @@ void obelisk::Fact::updateIsTrue(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; - auto result = sqlite3_prepare_v2(dbConnection, "UPDATE fact SET is_true=? WHERE id=?", -1, &ppStmt, nullptr); + auto result = sqlite3_prepare_v2(dbConnection, + "UPDATE fact SET is_true=? WHERE id=?", + -1, + &ppStmt, + nullptr); if (result != SQLITE_OK) { throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); @@ -338,7 +344,8 @@ void obelisk::Fact::updateIsTrue(sqlite3* dbConnection) // Row updated break; case SQLITE_CONSTRAINT : - throw obelisk::DatabaseConstraintException(sqlite3_errmsg(dbConnection)); + throw obelisk::DatabaseConstraintException( + sqlite3_errmsg(dbConnection)); case SQLITE_BUSY : throw obelisk::DatabaseBusyException(); break; diff --git a/src/lib/models/fact.h b/src/lib/models/fact.h index 1232224..6f8c7a6 100644 --- a/src/lib/models/fact.h +++ b/src/lib/models/fact.h @@ -10,7 +10,8 @@ namespace obelisk { /** - * @brief The Fact model represents truth in the releationship between two entities separated by a verb. + * @brief The Fact model represents truth in the releationship between two + * entities separated by a verb. * */ class Fact @@ -35,7 +36,8 @@ namespace obelisk obelisk::Entity rightEntity_; /** - * @brief The Verb that represents the relationship in the expression. + * @brief The Verb that represents the relationship in the + * expression. * */ obelisk::Verb verb_; @@ -77,12 +79,17 @@ 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] 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) : + Fact(obelisk::Entity leftEntity, + obelisk::Entity rightEntity, + obelisk::Verb verb, + bool isTrue = false) : id_(0), leftEntity_(leftEntity), rightEntity_(rightEntity), @@ -95,8 +102,10 @@ 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] 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. */ @@ -192,7 +201,8 @@ namespace obelisk void setIsTrue(bool isTrue); /** - * @brief Select the Fact from the KnowledgeBase by IDs of the sub-objects. + * @brief Select the Fact from the KnowledgeBase by IDs of the + * sub-objects. * * @param[in] dbConnection The database connection to use. */ @@ -206,7 +216,8 @@ namespace obelisk void insert(sqlite3* dbConnection); /** - * @brief Update whether or not the fact is true in the KnowledgeBase. + * @brief Update whether or not the fact is true in the + * KnowledgeBase. * * @param[in] dbConnection The database connection. */ diff --git a/src/lib/models/rule.cpp b/src/lib/models/rule.cpp index 9184b6c..fb2722c 100644 --- a/src/lib/models/rule.cpp +++ b/src/lib/models/rule.cpp @@ -111,8 +111,11 @@ void obelisk::Rule::insert(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; - auto result - = sqlite3_prepare_v2(dbConnection, "INSERT INTO rule (fact, reason) VALUES (?, ?)", -1, &ppStmt, nullptr); + auto result = sqlite3_prepare_v2(dbConnection, + "INSERT INTO rule (fact, reason) VALUES (?, ?)", + -1, + &ppStmt, + nullptr); if (result != SQLITE_OK) { throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); @@ -164,7 +167,8 @@ void obelisk::Rule::insert(sqlite3* dbConnection) sqlite3_set_last_insert_rowid(dbConnection, 0); break; case SQLITE_CONSTRAINT : - throw obelisk::DatabaseConstraintException(sqlite3_errmsg(dbConnection)); + throw obelisk::DatabaseConstraintException( + sqlite3_errmsg(dbConnection)); case SQLITE_BUSY : throw obelisk::DatabaseBusyException(); break; @@ -183,7 +187,9 @@ void obelisk::Rule::insert(sqlite3* dbConnection) } } -void obelisk::Rule::selectByReason(sqlite3* dbConnection, int reasonId, std::vector& rules) +void obelisk::Rule::selectByReason(sqlite3* dbConnection, + int reasonId, + std::vector& rules) { if (dbConnection == nullptr) { @@ -192,8 +198,11 @@ void obelisk::Rule::selectByReason(sqlite3* dbConnection, int reasonId, std::vec sqlite3_stmt* ppStmt = nullptr; - auto result - = sqlite3_prepare_v2(dbConnection, "SELECT id, fact, reason FROM rule WHERE (reason=?)", -1, &ppStmt, nullptr); + auto result = sqlite3_prepare_v2(dbConnection, + "SELECT id, fact, reason FROM rule WHERE (reason=?)", + -1, + &ppStmt, + nullptr); if (result != SQLITE_OK) { throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); diff --git a/src/lib/models/rule.h b/src/lib/models/rule.h index 04d3efb..efbdced 100644 --- a/src/lib/models/rule.h +++ b/src/lib/models/rule.h @@ -134,7 +134,8 @@ namespace obelisk void setReason(obelisk::Fact reason); /** - * @brief Select the Rule from the KnowledgeBase by IDs of the sub-objects. + * @brief Select the Rule from the KnowledgeBase by IDs of the + * sub-objects. * * @param[in] dbConnection The database connection to use. */ @@ -146,7 +147,9 @@ namespace obelisk * @param[in] dbConnection The database connection to use. * @param[out] rules The rules to fill in from the database. */ - static void selectByReason(sqlite3* dbConnection, int reasonId, std::vector& rules); + static void selectByReason(sqlite3* dbConnection, + int reasonId, + std::vector& rules); /** * @brief Insert the Rule into the KnowledgeBase. diff --git a/src/lib/models/suggest_action.cpp b/src/lib/models/suggest_action.cpp index f92a1c7..4226079 100644 --- a/src/lib/models/suggest_action.cpp +++ b/src/lib/models/suggest_action.cpp @@ -208,7 +208,8 @@ void obelisk::SuggestAction::insert(sqlite3* dbConnection) sqlite3_set_last_insert_rowid(dbConnection, 0); break; case SQLITE_CONSTRAINT : - throw obelisk::DatabaseConstraintException(sqlite3_errmsg(dbConnection)); + throw obelisk::DatabaseConstraintException( + sqlite3_errmsg(dbConnection)); case SQLITE_BUSY : throw obelisk::DatabaseBusyException(); break; diff --git a/src/lib/models/suggest_action.h b/src/lib/models/suggest_action.h index f6854e4..16d123e 100644 --- a/src/lib/models/suggest_action.h +++ b/src/lib/models/suggest_action.h @@ -9,7 +9,8 @@ namespace obelisk { /** - * @brief The SuggestAction model representas the actions to take depending on if the Fact is true or false. + * @brief The SuggestAction model representas the actions to take depending + * on if the Fact is true or false. * */ class SuggestAction @@ -72,7 +73,9 @@ namespace obelisk * @param[in] trueAction The true Action. * @param[in] falseAction The false Action. */ - SuggestAction(obelisk::Fact fact, obelisk::Action trueAction, obelisk::Action falseAction) : + SuggestAction(obelisk::Fact fact, + obelisk::Action trueAction, + obelisk::Action falseAction) : id_(0), fact_(fact), trueAction_(trueAction), @@ -88,7 +91,10 @@ namespace obelisk * @param[in] trueAction The true Action. * @param[in] falseAction The false Action. */ - SuggestAction(int id, obelisk::Fact fact, obelisk::Action trueAction, obelisk::Action falseAction) : + SuggestAction(int id, + obelisk::Fact fact, + obelisk::Action trueAction, + obelisk::Action falseAction) : id_(id), fact_(fact), trueAction_(trueAction), @@ -160,7 +166,8 @@ namespace obelisk void setFalseAction(obelisk::Action falseAction); /** - * @brief Select the SuggestAction from the KnowledgeBase by IDs of the sub-objects. + * @brief Select the SuggestAction from the KnowledgeBase by IDs of + * the sub-objects. * * @param[in] dbConnection The database connection to use. */ diff --git a/src/lib/models/verb.cpp b/src/lib/models/verb.cpp index 61b46c1..cd8c40b 100644 --- a/src/lib/models/verb.cpp +++ b/src/lib/models/verb.cpp @@ -23,7 +23,11 @@ void obelisk::Verb::selectByName(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; - auto result = sqlite3_prepare_v2(dbConnection, "SELECT id, name FROM verb WHERE name=?", -1, &ppStmt, nullptr); + auto result = sqlite3_prepare_v2(dbConnection, + "SELECT id, name FROM verb WHERE name=?", + -1, + &ppStmt, + nullptr); if (result != SQLITE_OK) { throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); @@ -85,13 +89,18 @@ void obelisk::Verb::insert(sqlite3* dbConnection) sqlite3_stmt* ppStmt = nullptr; - auto result = sqlite3_prepare_v2(dbConnection, "INSERT INTO verb (name) VALUES (?)", -1, &ppStmt, nullptr); + auto result = sqlite3_prepare_v2(dbConnection, + "INSERT INTO verb (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); + result + = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_TRANSIENT); switch (result) { case SQLITE_OK : @@ -118,7 +127,8 @@ void obelisk::Verb::insert(sqlite3* dbConnection) sqlite3_set_last_insert_rowid(dbConnection, 0); break; case SQLITE_CONSTRAINT : - throw obelisk::DatabaseConstraintException(sqlite3_errmsg(dbConnection)); + throw obelisk::DatabaseConstraintException( + sqlite3_errmsg(dbConnection)); case SQLITE_BUSY : throw obelisk::DatabaseBusyException(); break; diff --git a/src/lib/models/verb.h b/src/lib/models/verb.h index 2edc546..150e86f 100644 --- a/src/lib/models/verb.h +++ b/src/lib/models/verb.h @@ -8,7 +8,8 @@ namespace obelisk { /** - * @brief The Verb model represents a verb which is used to connnect entities. + * @brief The Verb model represents a verb which is used to connnect + * entities. * */ class Verb diff --git a/src/main.cpp b/src/main.cpp index e7f5e02..3aa2422 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,13 +10,15 @@ #include #include -int obelisk::mainLoop(const std::vector& sourceFiles, const std::string& kbFile) +int obelisk::mainLoop(const std::vector& sourceFiles, + const std::string& kbFile) { std::unique_ptr kb; try { - kb = std::unique_ptr {new obelisk::KnowledgeBase(kbFile.c_str())}; + kb = std::unique_ptr { + new obelisk::KnowledgeBase(kbFile.c_str())}; } catch (obelisk::KnowledgeBaseException& exception) { @@ -28,7 +30,8 @@ int obelisk::mainLoop(const std::vector& sourceFiles, const std::st std::shared_ptr lexer; try { - lexer = std::shared_ptr {new obelisk::Lexer(sourceFiles[file++])}; + lexer = std::shared_ptr { + new obelisk::Lexer(sourceFiles[file++])}; } catch (obelisk::LexerException& exception) { @@ -53,14 +56,16 @@ int obelisk::mainLoop(const std::vector& sourceFiles, const std::st switch (parser->getCurrentToken()) { case obelisk::Lexer::kTokenEof : - // end of source file found, create a new lexer and pass it to the parser to use + // end of source file found, create a new lexer and pass it to + // the parser to use if (file >= sourceFiles.size()) { return EXIT_SUCCESS; } try { - lexer = std::shared_ptr {new obelisk::Lexer(sourceFiles[file++])}; + lexer = std::shared_ptr { + new obelisk::Lexer(sourceFiles[file++])}; parser->setLexer(lexer); // prime the first token in the parser parser->getNextToken(); @@ -138,7 +143,11 @@ int main(int argc, char** argv) while (true) { int option_index = 0; - switch (getopt_long(argc, argv, "k:hv", obelisk::long_options, &option_index)) + switch (getopt_long(argc, + argv, + "k:hv", + obelisk::long_options, + &option_index)) { case 'k' : kbFile = std::string(optarg); diff --git a/src/main.h b/src/main.h index 3d79355..38e7a29 100644 --- a/src/main.h +++ b/src/main.h @@ -1,7 +1,8 @@ #include /** - * @brief The obelisk namespace contains everything needed to compile obelisk code. + * @brief The obelisk namespace contains everything needed to compile obelisk + * code. * */ namespace obelisk @@ -42,5 +43,6 @@ Options: * * @return int Returns EXIT_SUCCESS or EXIT_FAILURE. */ - int mainLoop(const std::vector &sourceFiles, const std::string &kbFile); + int mainLoop(const std::vector &sourceFiles, + const std::string &kbFile); } // namespace obelisk diff --git a/src/parser.cpp b/src/parser.cpp index b3207c9..907fa6b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -42,13 +42,15 @@ void obelisk::Parser::setCurrentToken(int currentToken) currentToken_ = currentToken; } -std::unique_ptr obelisk::Parser::logError(const char* str) +std::unique_ptr obelisk::Parser::logError( + const char* str) { fprintf(stderr, "Error: %s\n", str); return nullptr; } -std::unique_ptr obelisk::Parser::logErrorPrototype(const char* str) +std::unique_ptr obelisk::Parser::logErrorPrototype( + const char* str) { logError(str); return nullptr; @@ -82,12 +84,14 @@ std::unique_ptr obelisk::Parser::parsePrimary() std::unique_ptr obelisk::Parser::parseNumberExpression() { - auto result = std::make_unique(getLexer()->getNumberValue()); + auto result = std::make_unique( + getLexer()->getNumberValue()); getNextToken(); return result; } -std::unique_ptr obelisk::Parser::parseParenthesisExpression() +std::unique_ptr + obelisk::Parser::parseParenthesisExpression() { getNextToken(); auto v = parseExpression(); @@ -104,7 +108,8 @@ std::unique_ptr obelisk::Parser::parseParenthesisExpress return v; } -std::unique_ptr obelisk::Parser::parseIdentifierExpression() +std::unique_ptr + obelisk::Parser::parseIdentifierExpression() { std::string idName = getLexer()->getIdentifier(); getNextToken(); @@ -174,7 +179,8 @@ std::unique_ptr obelisk::Parser::parsePrototype() getNextToken(); - return std::make_unique(functionName, std::move(argNames)); + return std::make_unique(functionName, + std::move(argNames)); } std::unique_ptr obelisk::Parser::parseDefinition() @@ -188,7 +194,8 @@ std::unique_ptr obelisk::Parser::parseDefinition() if (auto expression = parseExpression()) { - return std::make_unique(std::move(prototype), std::move(expression)); + return std::make_unique(std::move(prototype), + std::move(expression)); } return nullptr; @@ -199,8 +206,10 @@ std::unique_ptr obelisk::Parser::parseTopLevelExpression() if (auto expression = parseExpression()) { // Make an anonymous prototype - auto prototype = std::make_unique("__anon_expr", std::vector()); - return std::make_unique(std::move(prototype), std::move(expression)); + auto prototype = std::make_unique("__anon_expr", + std::vector()); + return std::make_unique(std::move(prototype), + std::move(expression)); } return nullptr; } @@ -218,7 +227,8 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) getNextToken(); if (getCurrentToken() != '(') { - throw obelisk::ParserException("expected '(' but got '" + std::to_string(getCurrentToken()) + "'"); + throw obelisk::ParserException( + "expected '(' but got '" + std::to_string(getCurrentToken()) + "'"); } syntax.push('('); @@ -226,7 +236,8 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) getNextToken(); if (getLexer()->getIdentifier() != "if") { - throw obelisk::ParserException("expected 'if' but got '" + getLexer()->getIdentifier() + "'"); + throw obelisk::ParserException( + "expected 'if' but got '" + getLexer()->getIdentifier() + "'"); } bool getEntity {true}; @@ -302,7 +313,8 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) { if (!isalpha(letter)) { - throw new obelisk::ParserException("non alphabetic symbol in verb"); + throw new obelisk::ParserException( + "non alphabetic symbol in verb"); } } getEntity = true; @@ -357,7 +369,8 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) { if (getCurrentToken() == ')') { - // closing parenthesis found, make sure we have everything needed + // closing parenthesis found, make sure we have everything + // needed if (syntax.top() != '(') { throw obelisk::ParserException("unexpected ')'"); @@ -421,8 +434,9 @@ void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction) } } - suggestAction.setFact( - obelisk::Fact(obelisk::Entity(leftEntity), obelisk::Entity(rightEntity), obelisk::Verb(verb))); + suggestAction.setFact(obelisk::Fact(obelisk::Entity(leftEntity), + obelisk::Entity(rightEntity), + obelisk::Verb(verb))); suggestAction.setTrueAction(obelisk::Action(trueAction)); suggestAction.setFalseAction(obelisk::Action(falseAction)); } @@ -434,7 +448,8 @@ void obelisk::Parser::parseRule(obelisk::Rule& rule) getNextToken(); if (getCurrentToken() != '(') { - throw obelisk::ParserException("expected '(' but got '" + std::to_string(getCurrentToken()) + "'"); + throw obelisk::ParserException( + "expected '(' but got '" + std::to_string(getCurrentToken()) + "'"); } syntax.push('('); @@ -509,7 +524,8 @@ void obelisk::Parser::parseRule(obelisk::Rule& rule) { if (getCurrentToken() == ')') { - // closing parenthesis found, make sure we have everything needed + // closing parenthesis found, make sure we have everything + // needed if (syntax.top() != '(') { throw obelisk::ParserException("unexpected ')'"); @@ -536,12 +552,14 @@ void obelisk::Parser::parseRule(obelisk::Rule& rule) if (leftReasonEntity == "") { - throw obelisk::ParserException("missing left reason entity"); + throw obelisk::ParserException( + "missing left reason entity"); } if (rightReasonEntity == "") { - throw obelisk::ParserException("missing right reason entity"); + throw obelisk::ParserException( + "missing right reason entity"); } if (reasonVerb == "") @@ -579,7 +597,8 @@ void obelisk::Parser::parseRule(obelisk::Rule& rule) { if (!isalpha(letter)) { - throw new obelisk::ParserException("non alphabetic symbol in verb"); + throw new obelisk::ParserException( + "non alphabetic symbol in verb"); } } getEntity = true; @@ -592,7 +611,8 @@ void obelisk::Parser::parseRule(obelisk::Rule& rule) { if (!isalpha(letter)) { - throw new obelisk::ParserException("non alphabetic symbol in verb"); + throw new obelisk::ParserException( + "non alphabetic symbol in verb"); } } getEntity = true; @@ -602,7 +622,9 @@ void obelisk::Parser::parseRule(obelisk::Rule& rule) } } - rule.setFact(obelisk::Fact(obelisk::Entity(leftEntity), obelisk::Entity(rightEntity), obelisk::Verb(verb))); + rule.setFact(obelisk::Fact(obelisk::Entity(leftEntity), + obelisk::Entity(rightEntity), + obelisk::Verb(verb))); rule.setReason(obelisk::Fact(obelisk::Entity(leftReasonEntity), obelisk::Entity(rightReasonEntity), obelisk::Verb(reasonVerb))); @@ -615,7 +637,8 @@ void obelisk::Parser::parseFact(std::vector& facts) getNextToken(); if (getCurrentToken() != '(') { - throw obelisk::ParserException("expected '(' but got '" + std::to_string(getCurrentToken()) + "'"); + throw obelisk::ParserException( + "expected '(' but got '" + std::to_string(getCurrentToken()) + "'"); } syntax.push('('); @@ -671,7 +694,8 @@ void obelisk::Parser::parseFact(std::vector& facts) { if (getCurrentToken() == ')') { - // closing parenthesis found, make sure we have everything needed + // closing parenthesis found, make sure we have everything + // needed if (syntax.top() != '(') { throw obelisk::ParserException("unexpected ')'"); @@ -688,12 +712,14 @@ void obelisk::Parser::parseFact(std::vector& facts) if (leftEntities.size() == 0) { - throw obelisk::ParserException("missing left side entities"); + throw obelisk::ParserException( + "missing left side entities"); } if (rightEntities.size() == 0) { - throw obelisk::ParserException("missing right side entities"); + throw obelisk::ParserException( + "missing right side entities"); } getNextToken(); @@ -723,7 +749,8 @@ void obelisk::Parser::parseFact(std::vector& facts) { if (!isalpha(letter)) { - throw new obelisk::ParserException("non alphabetic symbol in verb"); + throw new obelisk::ParserException( + "non alphabetic symbol in verb"); } } getEntity = true; @@ -736,8 +763,10 @@ void obelisk::Parser::parseFact(std::vector& facts) { for (auto& rightEntity : rightEntities) { - facts.push_back( - obelisk::Fact(obelisk::Entity(leftEntity), obelisk::Entity(rightEntity), obelisk::Verb(verb), true)); + facts.push_back(obelisk::Fact(obelisk::Entity(leftEntity), + obelisk::Entity(rightEntity), + obelisk::Verb(verb), + true)); } } } @@ -850,7 +879,8 @@ void obelisk::Parser::handleFact(std::unique_ptr& kb) } } -void obelisk::Parser::insertEntity(std::unique_ptr& kb, obelisk::Entity& entity) +void obelisk::Parser::insertEntity(std::unique_ptr& kb, + obelisk::Entity& entity) { std::vector entities {entity}; kb->addEntities(entities); @@ -862,12 +892,14 @@ void obelisk::Parser::insertEntity(std::unique_ptr& kb, kb->getEntity(entity); if (entity.getId() == 0) { - throw obelisk::ParserException("entity could not be inserted into the database"); + throw obelisk::ParserException( + "entity could not be inserted into the database"); } } } -void obelisk::Parser::insertVerb(std::unique_ptr& kb, obelisk::Verb& verb) +void obelisk::Parser::insertVerb(std::unique_ptr& kb, + obelisk::Verb& verb) { std::vector verbs {verb}; kb->addVerbs(verbs); @@ -879,12 +911,14 @@ void obelisk::Parser::insertVerb(std::unique_ptr& kb, ob kb->getVerb(verb); if (verb.getId() == 0) { - throw obelisk::ParserException("verb could not be inserted into the database"); + throw obelisk::ParserException( + "verb could not be inserted into the database"); } } } -void obelisk::Parser::insertAction(std::unique_ptr& kb, obelisk::Action& action) +void obelisk::Parser::insertAction(std::unique_ptr& kb, + obelisk::Action& action) { std::vector actions {action}; kb->addActions(actions); @@ -896,12 +930,15 @@ void obelisk::Parser::insertAction(std::unique_ptr& kb, kb->getAction(action); if (action.getId() == 0) { - throw obelisk::ParserException("action could not be inserted into the database"); + throw obelisk::ParserException( + "action could not be inserted into the database"); } } } -void obelisk::Parser::insertFact(std::unique_ptr& kb, obelisk::Fact& fact, bool updateIsTrue) +void obelisk::Parser::insertFact(std::unique_ptr& kb, + obelisk::Fact& fact, + bool updateIsTrue) { std::vector facts {fact}; kb->addFacts(facts); @@ -913,7 +950,8 @@ void obelisk::Parser::insertFact(std::unique_ptr& kb, ob kb->getFact(fact); if (fact.getId() == 0) { - throw obelisk::ParserException("fact could not be inserted into the database"); + throw obelisk::ParserException( + "fact could not be inserted into the database"); } else { @@ -926,7 +964,8 @@ void obelisk::Parser::insertFact(std::unique_ptr& kb, ob } } -void obelisk::Parser::insertSuggestAction(std::unique_ptr& kb, +void obelisk::Parser::insertSuggestAction( + std::unique_ptr& kb, obelisk::SuggestAction& suggestAction) { std::vector suggestActions {suggestAction}; @@ -939,12 +978,14 @@ void obelisk::Parser::insertSuggestAction(std::unique_ptrgetSuggestAction(suggestAction); if (suggestAction.getId() == 0) { - throw obelisk::ParserException("suggest_action could not be inserted into the database"); + throw obelisk::ParserException( + "suggest_action could not be inserted into the database"); } } } -void obelisk::Parser::insertRule(std::unique_ptr& kb, obelisk::Rule& rule) +void obelisk::Parser::insertRule(std::unique_ptr& kb, + obelisk::Rule& rule) { std::vector rules {rule}; kb->addRules(rules); @@ -956,7 +997,8 @@ void obelisk::Parser::insertRule(std::unique_ptr& kb, ob kb->getRule(rule); if (rule.getId() == 0) { - throw obelisk::ParserException("rule could not be inserted into the database"); + throw obelisk::ParserException( + "rule could not be inserted into the database"); } } } diff --git a/src/parser.h b/src/parser.h index 436fa13..34b4abf 100644 --- a/src/parser.h +++ b/src/parser.h @@ -18,14 +18,16 @@ namespace obelisk { /** - * @brief The Parser is responsible for analyzing the language's key words and taking action based on its analysis. + * @brief The Parser is responsible for analyzing the language's key words + * and taking action based on its analysis. * */ class Parser { private: /** - * @brief The Lexer object that the Parser is using to Parse a specific source file. + * @brief The Lexer object that the Parser is using to Parse a + * specific source file. * */ std::shared_ptr lexer_; @@ -47,7 +49,8 @@ namespace obelisk * @brief Log errors from the LLVM parsing. * * @param[in] str The error message. - * @return std::unique_ptr Returns the AST expression that caused the error. + * @return std::unique_ptr Returns the AST + * expression that caused the error. */ std::unique_ptr logError(const char* str); @@ -55,70 +58,82 @@ namespace obelisk * @brief Log errors from the LLVM parsing involving the prototypes. * * @param[in] str The error message. - * @return std::unique_ptr Returns the AST for the prototype. + * @return std::unique_ptr Returns the AST + * for the prototype. */ - std::unique_ptr logErrorPrototype(const char* str); + std::unique_ptr logErrorPrototype( + const char* str); /** * @brief The AST expression parser. * - * @return std::unique_ptr Returns the parsed AST expression. + * @return std::unique_ptr Returns the + * parsed AST expression. */ std::unique_ptr parseExpression(); /** * @brief The AST number expression parser. * - * @return std::unique_ptr Returns the parsed AST expression. + * @return std::unique_ptr Returns the + * parsed AST expression. */ std::unique_ptr parseNumberExpression(); /** * @brief The AST parenthesis expression parser. * - * @return std::unique_ptr Returns the parsed AST expression. + * @return std::unique_ptr Returns the + * parsed AST expression. */ - std::unique_ptr parseParenthesisExpression(); + std::unique_ptr + parseParenthesisExpression(); /** * @brief The AST identifier expression parser. * - * @return std::unique_ptr Returns the parsed AST expression. + * @return std::unique_ptr Returns the + * parsed AST expression. */ std::unique_ptr parseIdentifierExpression(); /** * @brief The AST primary expression parser. * - * @return std::unique_ptr Returns the parsed AST expression. + * @return std::unique_ptr Returns the + * parsed AST expression. */ std::unique_ptr parsePrimary(); /** * @brief The AST prototype parser. * - * @return std::unique_ptr Returns the parsed AST prototype expression. + * @return std::unique_ptr Returns the parsed + * AST prototype expression. */ std::unique_ptr parsePrototype(); /** * @brief The AST definition parser. * - * @return std::unique_ptr Returns the parsed AST definition expression. + * @return std::unique_ptr Returns the parsed + * AST definition expression. */ std::unique_ptr parseDefinition(); /** * @brief The AST top level expression parser. * - * @return std::unique_ptr Returns the parsed AST top level expression. + * @return std::unique_ptr Returns the parsed + * AST top level expression. */ std::unique_ptr parseTopLevelExpression(); /** * @brief The AST external definition parser. * - * @return std::unique_ptr Returns the parsed AST external definition. + * @return std::unique_ptr Returns the parsed + * AST external definition. */ std::unique_ptr parseExtern(); @@ -147,7 +162,8 @@ namespace obelisk /** * @brief Construct a new Parser object. * - * @param[in] lexer The lexer the parser uses to retrieve parts of the language. + * @param[in] lexer The lexer the parser uses to retrieve parts of + * the language. */ Parser(std::shared_ptr lexer) : lexer_(lexer) @@ -157,7 +173,8 @@ namespace obelisk /** * @brief Get the Lexer. * - * @return std::shared_ptr Returns the current Lexer in use by the Parser. + * @return std::shared_ptr Returns the current Lexer + * in use by the Parser. */ std::shared_ptr getLexer(); @@ -183,7 +200,8 @@ namespace obelisk int getNextToken(); /** - * @brief Parse the SuggestAction and then insert it into the KnowledgeBase. + * @brief Parse the SuggestAction and then insert it into the + * KnowledgeBase. * * @param[in] kb The KnowledgeBase to insert the SuggestAction into. */ @@ -207,55 +225,66 @@ namespace obelisk * @brief Helper used to insert an Entity into the KnowledgeBase. * * @param[in] kb The KnowledgeBase to use. - * @param[in,out] entity The Entity to insert. It will contain the ID of the Entity after inserting it. + * @param[in,out] entity The Entity to insert. It will contain the + * ID of the Entity after inserting it. */ - void insertEntity(std::unique_ptr& kb, obelisk::Entity& entity); + void insertEntity(std::unique_ptr& kb, + obelisk::Entity& entity); /** * @brief Helper used to insert a Verb into the KnowledgeBase. * * @param[in] kb The KnowledegeBase to use. - * @param[in,out] verb The Verb to insert. It will contain the ID of the Verb after inserting it. + * @param[in,out] verb The Verb to insert. It will contain the ID of + * the Verb after inserting it. */ - void insertVerb(std::unique_ptr& kb, obelisk::Verb& verb); + void insertVerb(std::unique_ptr& kb, + obelisk::Verb& verb); /** * @brief Helper used to insert an Action into the KnowledgeBase. * * @param[in] kb The KnowledgeBase to use. - * @param[in,out] action The Action to insert. It will contain the ID of the Action after inserting it. + * @param[in,out] action The Action to insert. It will contain the + * ID of the Action after inserting it. */ - void insertAction(std::unique_ptr& kb, obelisk::Action& action); + void insertAction(std::unique_ptr& kb, + obelisk::Action& action); /** * @brief Helper used to insert a Fact into the KnowledgeBase. * * @param[in] kb The KnowledgeBase to use. - * @param[in,out] fact The Fact to insert. It will contain the ID of the Fact after inserting it. - * @param[in] updateIsTrue If true, it will update the value of is_true in the KnowledgeBase if the Fact - * already exists. + * @param[in,out] fact The Fact to insert. It will contain the ID of + * the Fact after inserting it. + * @param[in] updateIsTrue If true, it will update the value of + * is_true in the KnowledgeBase if the Fact already exists. */ void insertFact(std::unique_ptr& kb, obelisk::Fact& fact, bool updateIsTrue = false); /** - * @brief Helper used to insert a SuggestAction into the KnowledgeBase. + * @brief Helper used to insert a SuggestAction into the + * KnowledgeBase. * * @param[in] kb The KnowledgeBase to use. - * @param[in,out] suggestAction The SuggestAction to insert. It will contain the ID of the SuggestAction - * after inserting it. + * @param[in,out] suggestAction The SuggestAction to insert. It will + * contain the ID of the SuggestAction after inserting it. */ - void insertSuggestAction(std::unique_ptr& kb, + void insertSuggestAction( + std::unique_ptr& kb, obelisk::SuggestAction& suggestAction); /** * @brief Helper usedto insert a Rule into the KnowledgeBase. * * @param[in] kb The KnowledgeBase to use. - * @param[in,out] rule The Rule to insert. It will contain the ID of the Rule after inserting it. + * @param[in,out] rule The Rule to insert. It will contain the ID of + * the Rule after inserting it. */ - void insertRule(std::unique_ptr& kb, obelisk::Rule& rule); + void insertRule(std::unique_ptr& kb, + obelisk::Rule& rule); }; /** From 0e9cc0f832d0962e748e37b92e52b78b804daf4b Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Tue, 21 Feb 2023 09:09:48 -0300 Subject: [PATCH 23/27] create a C wrapper for obelisk --- meson.build | 1 + src/lib/include/obelisk.h | 44 ++++++++++++++++++++++++++++++ src/lib/include/obelisk_c.h | 28 +++++++++++++++++++ src/lib/include/obelisk_wrapper.h | 45 +++++++++++++++++++++++++++++++ src/lib/meson.build | 2 ++ src/lib/obelisk.c | 23 ++++++++++++++++ src/lib/obelisk.cpp | 12 +++++++++ src/lib/obelisk_wrapper.cpp | 32 ++++++++++++++++++++++ src/lib/version.h.in | 5 ++++ src/main.h | 5 ++++ 10 files changed, 197 insertions(+) create mode 100644 src/lib/include/obelisk_c.h create mode 100644 src/lib/include/obelisk_wrapper.h create mode 100644 src/lib/obelisk.c create mode 100644 src/lib/obelisk_wrapper.cpp diff --git a/meson.build b/meson.build index 4f3a936..e27febf 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,5 @@ project('obelisk', + 'c', 'cpp', version : '1.0.0', license : 'BSD-3-Clause', diff --git a/src/lib/include/obelisk.h b/src/lib/include/obelisk.h index b06eb8b..afa02ad 100644 --- a/src/lib/include/obelisk.h +++ b/src/lib/include/obelisk.h @@ -1,3 +1,6 @@ +#ifndef OBELISK_INCLUDE_OBELISK_H +#define OBELISK_INCLUDE_OBELISK_H + #include /** @@ -15,6 +18,18 @@ namespace obelisk class Obelisk { public: + /** + * @brief Construct a new Obelisk object. + * + */ + Obelisk() = default; + + /** + * @brief Destroy the Obelisk object. + * + */ + ~Obelisk() = default; + /** * @brief Get the obelisk version. * @@ -28,5 +43,34 @@ namespace obelisk * @return int The version. */ int getLibVersion(); + + /** + * @brief Query the Obelisk KnowledgeBase to see if the Fact is true + * or not. + * + * @param[in] leftEntity The left entity. + * @param[in] verb The verb. + * @param[in] rightEntity The right entity. + * @return double Returns a value between 0 and 1 depending on + * whether it is true or false. + */ + double query(const std::string& leftEntity, + const std::string& verb, + const std::string& rightEntity); + + /** + * @brief Query the Obelisk KnowledgeBase and return the suggested + * action to take. + * + * @param[in] leftEntity The left entity. + * @param[in] verb The verb. + * @param[in] rightEntity The right entity. + * @return std::string Returns the suggested action. + */ + std::string query_action(const std::string& leftEntity, + const std::string& verb, + const std::string& rightEntity); }; } // namespace obelisk + +#endif diff --git a/src/lib/include/obelisk_c.h b/src/lib/include/obelisk_c.h new file mode 100644 index 0000000..616b7e2 --- /dev/null +++ b/src/lib/include/obelisk_c.h @@ -0,0 +1,28 @@ +#ifndef OBELISK_INCLUDE_OBELISK_PROGRAM_H +#define OBELISK_INCLUDE_OBELISK_PROGRAM_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Get the obelisk version. + * + * @return const char* Returns a string containing the version. This must be + * freed by the caller. + */ + extern const char* obelisk_get_version(); + + /** + * @brief Get the obelisk library so version. + * + * @return int Returns the so version. + */ + extern int obelisk_get_lib_version(); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/lib/include/obelisk_wrapper.h b/src/lib/include/obelisk_wrapper.h new file mode 100644 index 0000000..737c129 --- /dev/null +++ b/src/lib/include/obelisk_wrapper.h @@ -0,0 +1,45 @@ +#ifndef OBELISK_INCLUDE_OBELISK_WRAPPER_H +#define OBELISK_INCLUDE_OBELISK_WRAPPER_H + +typedef struct CObelisk CObelisk; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Create a obelisk object. + * + * @return CObelisk* Returns the obelisk object. + */ + CObelisk *create_obelisk(); + + /** + * @brief Calls the obelisk method getVersion. + * + * @param[in] p_obelisk The obelisk object pointer. + * @return const char* Returns the version. Must be freed by caller. + */ + const char *call_obelisk_getVersion(CObelisk *p_obelisk); + + /** + * @brief Calls the obelisk method getLibVersion. + * + * @param[in] p_obelisk The obelisk object pointer. + * @return int Returns the library so version. + */ + int call_obelisk_getLibVersion(CObelisk *p_obelisk); + + /** + * @brief Delete a obelisk object. + * + * @param[in] p_obelisk The obelisk object pointer. + */ + void destroy_obelisk(CObelisk *p_obelisk); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/lib/meson.build b/src/lib/meson.build index 30dff63..70f39d5 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -10,6 +10,8 @@ subdir('models') obelisk_lib_sources = files( 'obelisk.cpp', + 'obelisk.c', + 'obelisk_wrapper.cpp', 'knowledge_base.cpp' ) diff --git a/src/lib/obelisk.c b/src/lib/obelisk.c new file mode 100644 index 0000000..228d9d2 --- /dev/null +++ b/src/lib/obelisk.c @@ -0,0 +1,23 @@ +#include "obelisk_c.h" +#include "obelisk_wrapper.h" + +#include +#include + +const char* obelisk_get_version() +{ + CObelisk* obelisk = create_obelisk(); + size_t len = strlen(call_obelisk_getVersion(obelisk)) + 1; + char* version = malloc(len); + memcpy(version, call_obelisk_getVersion(obelisk), len); + destroy_obelisk(obelisk); + return version; +} + +int obelisk_get_lib_version() +{ + CObelisk* obelisk = create_obelisk(); + int version = call_obelisk_getLibVersion(obelisk); + destroy_obelisk(obelisk); + return version; +} diff --git a/src/lib/obelisk.cpp b/src/lib/obelisk.cpp index e5b979a..c0eb5bf 100644 --- a/src/lib/obelisk.cpp +++ b/src/lib/obelisk.cpp @@ -10,3 +10,15 @@ int obelisk::Obelisk::getLibVersion() { return obelisk::soVersion; } + +double obelisk::Obelisk::query(const std::string& leftEntity, + const std::string& verb, + const std::string& rightEntity) +{ +} + +std::string obelisk::Obelisk::query_action(const std::string& leftEntity, + const std::string& verb, + const std::string& rightEntity) +{ +} diff --git a/src/lib/obelisk_wrapper.cpp b/src/lib/obelisk_wrapper.cpp new file mode 100644 index 0000000..0720ae1 --- /dev/null +++ b/src/lib/obelisk_wrapper.cpp @@ -0,0 +1,32 @@ +#include "obelisk.h" +#include "obelisk_wrapper.h" + +extern "C" +{ + CObelisk* create_obelisk() + { + obelisk::Obelisk* obelisk = new obelisk::Obelisk(); + return reinterpret_cast(obelisk); + } + + const char* call_obelisk_getVersion(CObelisk* p_obelisk) + { + obelisk::Obelisk* obelisk + = reinterpret_cast(p_obelisk); + return obelisk->getVersion().c_str(); + } + + int call_obelisk_getLibVersion(CObelisk* p_obelisk) + { + obelisk::Obelisk* obelisk + = reinterpret_cast(p_obelisk); + return obelisk->getLibVersion(); + } + + void destroy_obelisk(CObelisk* p_obelisk) + { + obelisk::Obelisk* obelisk + = reinterpret_cast(p_obelisk); + delete obelisk; + } +}; diff --git a/src/lib/version.h.in b/src/lib/version.h.in index b6b0867..c9aee59 100644 --- a/src/lib/version.h.in +++ b/src/lib/version.h.in @@ -1,3 +1,6 @@ +#ifndef OBELISK_VERSION_H +#define OBELISK_VERSION_H + namespace obelisk { /** @@ -14,3 +17,5 @@ namespace obelisk const int soVersion = @so_version@; // clang-format on } // namespace obelisk + +#endif diff --git a/src/main.h b/src/main.h index 38e7a29..b330b53 100644 --- a/src/main.h +++ b/src/main.h @@ -1,3 +1,6 @@ +#ifndef OBELISK_MAIN_H +#define OBELISK_MAIN_H + #include /** @@ -46,3 +49,5 @@ Options: int mainLoop(const std::vector &sourceFiles, const std::string &kbFile); } // namespace obelisk + +#endif From e665ff044f578999ea799d738215201e9a610f51 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Tue, 21 Feb 2023 21:31:04 -0300 Subject: [PATCH 24/27] improvements to the C wrapper and close a memory leak --- src/lib/include/obelisk_c.h | 26 +++++++++++++++++++++++-- src/lib/obelisk.c | 24 ++++++++++++----------- src/lib/obelisk_wrapper.cpp | 7 +++++-- src/lib/{include => }/obelisk_wrapper.h | 7 ++++--- 4 files changed, 46 insertions(+), 18 deletions(-) rename src/lib/{include => }/obelisk_wrapper.h (82%) diff --git a/src/lib/include/obelisk_c.h b/src/lib/include/obelisk_c.h index 616b7e2..437d088 100644 --- a/src/lib/include/obelisk_c.h +++ b/src/lib/include/obelisk_c.h @@ -1,25 +1,47 @@ #ifndef OBELISK_INCLUDE_OBELISK_PROGRAM_H #define OBELISK_INCLUDE_OBELISK_PROGRAM_H +/** + * @brief Struct wrapper around Obelisk class. + * + */ +typedef struct CObelisk CObelisk; + #ifdef __cplusplus extern "C" { #endif + /** + * @brief Create an obelisk object. + * + * @return CObelisk* Returns an obelisk object. + */ + extern CObelisk* obelisk_open(); + + /** + * @brief Delete an obelisk object. + * + * @param[in] obelisk The obelisk object. + */ + extern void obelisk_close(CObelisk* obelisk); + /** * @brief Get the obelisk version. * + * @param[in] obelisk The obelisk object. * @return const char* Returns a string containing the version. This must be * freed by the caller. */ - extern const char* obelisk_get_version(); + extern char* obelisk_get_version(CObelisk* obelisk); /** * @brief Get the obelisk library so version. * + * @param[in] obelisk The obelisk object. * @return int Returns the so version. */ - extern int obelisk_get_lib_version(); + extern int obelisk_get_lib_version(CObelisk* obelisk); #ifdef __cplusplus }; diff --git a/src/lib/obelisk.c b/src/lib/obelisk.c index 228d9d2..5d959b7 100644 --- a/src/lib/obelisk.c +++ b/src/lib/obelisk.c @@ -4,20 +4,22 @@ #include #include -const char* obelisk_get_version() +CObelisk* obelisk_open() { - CObelisk* obelisk = create_obelisk(); - size_t len = strlen(call_obelisk_getVersion(obelisk)) + 1; - char* version = malloc(len); - memcpy(version, call_obelisk_getVersion(obelisk), len); - destroy_obelisk(obelisk); - return version; + return create_obelisk(); } -int obelisk_get_lib_version() +void obelisk_close(CObelisk* obelisk) { - CObelisk* obelisk = create_obelisk(); - int version = call_obelisk_getLibVersion(obelisk); destroy_obelisk(obelisk); - return version; +} + +char* obelisk_get_version(CObelisk* obelisk) +{ + return call_obelisk_getVersion(obelisk); +} + +int obelisk_get_lib_version(CObelisk* obelisk) +{ + return call_obelisk_getLibVersion(obelisk); } diff --git a/src/lib/obelisk_wrapper.cpp b/src/lib/obelisk_wrapper.cpp index 0720ae1..625609b 100644 --- a/src/lib/obelisk_wrapper.cpp +++ b/src/lib/obelisk_wrapper.cpp @@ -1,6 +1,8 @@ #include "obelisk.h" #include "obelisk_wrapper.h" +#include + extern "C" { CObelisk* create_obelisk() @@ -9,11 +11,12 @@ extern "C" return reinterpret_cast(obelisk); } - const char* call_obelisk_getVersion(CObelisk* p_obelisk) + char* call_obelisk_getVersion(CObelisk* p_obelisk) { obelisk::Obelisk* obelisk = reinterpret_cast(p_obelisk); - return obelisk->getVersion().c_str(); + auto version = strdup(obelisk->getVersion().c_str()); + return version; } int call_obelisk_getLibVersion(CObelisk* p_obelisk) diff --git a/src/lib/include/obelisk_wrapper.h b/src/lib/obelisk_wrapper.h similarity index 82% rename from src/lib/include/obelisk_wrapper.h rename to src/lib/obelisk_wrapper.h index 737c129..c697816 100644 --- a/src/lib/include/obelisk_wrapper.h +++ b/src/lib/obelisk_wrapper.h @@ -1,7 +1,7 @@ #ifndef OBELISK_INCLUDE_OBELISK_WRAPPER_H #define OBELISK_INCLUDE_OBELISK_WRAPPER_H -typedef struct CObelisk CObelisk; +#include "include/obelisk_c.h" #ifdef __cplusplus extern "C" @@ -19,9 +19,10 @@ extern "C" * @brief Calls the obelisk method getVersion. * * @param[in] p_obelisk The obelisk object pointer. - * @return const char* Returns the version. Must be freed by caller. + * @return const char* Returns the version. This must be freed by the + * caller. */ - const char *call_obelisk_getVersion(CObelisk *p_obelisk); + char *call_obelisk_getVersion(CObelisk *p_obelisk); /** * @brief Calls the obelisk method getLibVersion. From d84f2a944ca0909246b960e4fe4337c9903b98bf Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Wed, 22 Feb 2023 00:42:40 -0300 Subject: [PATCH 25/27] allow the library to query the knowledge base --- src/lib/include/obelisk.h | 18 ++++-- src/lib/include/obelisk_c.h | 17 ++++- src/lib/knowledge_base.cpp | 9 ++- src/lib/knowledge_base.h | 2 + src/lib/models/fact.cpp | 123 +++++++++++++++++++++++++++++++++++- src/lib/models/fact.h | 18 ++++-- src/lib/obelisk.c | 12 +++- src/lib/obelisk.cpp | 14 ++++ src/lib/obelisk_wrapper.cpp | 16 ++++- src/lib/obelisk_wrapper.h | 17 ++++- src/parser.cpp | 6 +- 11 files changed, 228 insertions(+), 24 deletions(-) diff --git a/src/lib/include/obelisk.h b/src/lib/include/obelisk.h index afa02ad..b078614 100644 --- a/src/lib/include/obelisk.h +++ b/src/lib/include/obelisk.h @@ -1,6 +1,9 @@ #ifndef OBELISK_INCLUDE_OBELISK_H #define OBELISK_INCLUDE_OBELISK_H +#include "knowledge_base.h" + +#include #include /** @@ -17,12 +20,15 @@ namespace obelisk */ class Obelisk { + private: + std::unique_ptr kb_; + public: /** * @brief Construct a new Obelisk object. * */ - Obelisk() = default; + Obelisk(std::string filename); /** * @brief Destroy the Obelisk object. @@ -45,14 +51,14 @@ namespace obelisk int getLibVersion(); /** - * @brief Query the Obelisk KnowledgeBase to see if the Fact is true + * @brief Query the obelisk KnowledgeBase to see if a Fact is true * or not. * - * @param[in] leftEntity The left entity. + * @param[in] p_obelisk The obelisk object pointer. + * @param[in] left_entity The left entity. * @param[in] verb The verb. - * @param[in] rightEntity The right entity. - * @return double Returns a value between 0 and 1 depending on - * whether it is true or false. + * @param[in] right_entity The right entity. + * @return double Returns whether or not the Fact is true. */ double query(const std::string& leftEntity, const std::string& verb, diff --git a/src/lib/include/obelisk_c.h b/src/lib/include/obelisk_c.h index 437d088..fa5ab83 100644 --- a/src/lib/include/obelisk_c.h +++ b/src/lib/include/obelisk_c.h @@ -15,9 +15,10 @@ extern "C" /** * @brief Create an obelisk object. * + * @param[in] filename The obelisk KnowledgeBase file to use. * @return CObelisk* Returns an obelisk object. */ - extern CObelisk* obelisk_open(); + extern CObelisk* obelisk_open(const char* filename); /** * @brief Delete an obelisk object. @@ -35,6 +36,20 @@ extern "C" */ extern char* obelisk_get_version(CObelisk* obelisk); + /** + * @brief Query the obelisk KnowledgeBase to see if a Fact is true or false. + * + * @param[in] obelisk The obelisk object. + * @param[in] left_entity The left entity. + * @param[in] verb The verb. + * @param[in] right_entity The right entity. + * @return double Returns whether the Fact is true or false. + */ + extern double obelisk_query(CObelisk* obelisk, + const char* left_entity, + const char* verb, + const char* right_entity); + /** * @brief Get the obelisk library so version. * diff --git a/src/lib/knowledge_base.cpp b/src/lib/knowledge_base.cpp index 52fa521..ec345df 100644 --- a/src/lib/knowledge_base.cpp +++ b/src/lib/knowledge_base.cpp @@ -245,10 +245,10 @@ void obelisk::KnowledgeBase::checkRule(obelisk::Fact& fact) { auto reason = rule.getReason(); getFact(reason); - if (reason.getIsTrue()) + if (reason.getIsTrue() > 0) { auto updateFact = rule.getFact(); - updateFact.setIsTrue(true); + updateFact.setIsTrue(1.0); updateFact.updateIsTrue(dbConnection_); } } @@ -259,6 +259,11 @@ void obelisk::KnowledgeBase::updateIsTrue(obelisk::Fact& fact) fact.updateIsTrue(dbConnection_); } +void obelisk::KnowledgeBase::queryFact(obelisk::Fact& fact) +{ + fact.selectByName(dbConnection_); +} + void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) diff --git a/src/lib/knowledge_base.h b/src/lib/knowledge_base.h index 4fe6b6a..24ae8bf 100644 --- a/src/lib/knowledge_base.h +++ b/src/lib/knowledge_base.h @@ -200,6 +200,8 @@ namespace obelisk */ void updateIsTrue(obelisk::Fact& fact); + void queryFact(obelisk::Fact& fact); + /** * @brief Take a float and divide it into 2 floats. * diff --git a/src/lib/models/fact.cpp b/src/lib/models/fact.cpp index ed6dad5..5bfc1ca 100644 --- a/src/lib/models/fact.cpp +++ b/src/lib/models/fact.cpp @@ -157,6 +157,125 @@ void obelisk::Fact::selectById(sqlite3* dbConnection) } } +void obelisk::Fact::selectByName(sqlite3* dbConnection) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, + "SELECT fact.id, fact.left_entity, fact.right_entity, fact.verb, fact.is_true FROM fact LEFT JOIN entity le ON le.id = fact.left_entity LEFT JOIN entity re ON re.id = fact.right_entity LEFT JOIN verb v ON fact.verb = v.id WHERE (le.name=? AND v.name=? AND re.name=?)", + -1, + &ppStmt, + nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result = sqlite3_bind_text(ppStmt, + 1, + getLeftEntity().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_bind_text(ppStmt, + 2, + getVerb().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_bind_text(ppStmt, + 3, + getRightEntity().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)); + getLeftEntity().setId(sqlite3_column_int(ppStmt, 1)); + getRightEntity().setId(sqlite3_column_int(ppStmt, 2)); + getVerb().setId(sqlite3_column_int(ppStmt, 3)); + setIsTrue(sqlite3_column_int(ppStmt, 4)); + 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::Fact::insert(sqlite3* dbConnection) { if (dbConnection == nullptr) @@ -404,12 +523,12 @@ void obelisk::Fact::setVerb(obelisk::Verb verb) verb_ = verb; } -bool& obelisk::Fact::getIsTrue() +double& obelisk::Fact::getIsTrue() { return isTrue_; } -void obelisk::Fact::setIsTrue(bool isTrue) +void obelisk::Fact::setIsTrue(double isTrue) { isTrue_ = isTrue; } diff --git a/src/lib/models/fact.h b/src/lib/models/fact.h index 6f8c7a6..f920ef4 100644 --- a/src/lib/models/fact.h +++ b/src/lib/models/fact.h @@ -46,7 +46,7 @@ namespace obelisk * @brief Whether or not the fact is considered true or not. * */ - bool isTrue_; + double isTrue_; public: /** @@ -89,7 +89,7 @@ namespace obelisk Fact(obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb, - bool isTrue = false) : + double isTrue = 0) : id_(0), leftEntity_(leftEntity), rightEntity_(rightEntity), @@ -113,7 +113,7 @@ namespace obelisk obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb, - bool isTrue = false) : + double isTrue = 0) : id_(id), leftEntity_(leftEntity), rightEntity_(rightEntity), @@ -191,14 +191,14 @@ namespace obelisk * @return true If the Fact is considered true. * @return false If the Fact is considered false. */ - bool& getIsTrue(); + double& getIsTrue(); /** * @brief Set the Fact as true or false. * * @param[in] isTrue Whether or not the Fact is true. */ - void setIsTrue(bool isTrue); + void setIsTrue(double isTrue); /** * @brief Select the Fact from the KnowledgeBase by IDs of the @@ -208,6 +208,14 @@ namespace obelisk */ void selectById(sqlite3* dbConnection); + /** + * @brief Select the Fact from the KnowledgeBase by the name's of + * the entities and verb. + * + * @param[in] dbConnection The database connection to use. + */ + void selectByName(sqlite3* dbConnection); + /** * @brief Insert the Fact into the KnowledgeBase. * diff --git a/src/lib/obelisk.c b/src/lib/obelisk.c index 5d959b7..9de3ded 100644 --- a/src/lib/obelisk.c +++ b/src/lib/obelisk.c @@ -4,9 +4,9 @@ #include #include -CObelisk* obelisk_open() +CObelisk* obelisk_open(const char* filename) { - return create_obelisk(); + return create_obelisk(filename); } void obelisk_close(CObelisk* obelisk) @@ -23,3 +23,11 @@ int obelisk_get_lib_version(CObelisk* obelisk) { return call_obelisk_getLibVersion(obelisk); } + +double obelisk_query(CObelisk* obelisk, + const char* left_entity, + const char* verb, + const char* right_entity) +{ + return call_obelisk_query(obelisk, left_entity, verb, right_entity); +} diff --git a/src/lib/obelisk.cpp b/src/lib/obelisk.cpp index c0eb5bf..289e22d 100644 --- a/src/lib/obelisk.cpp +++ b/src/lib/obelisk.cpp @@ -1,6 +1,12 @@ #include "include/obelisk.h" #include "version.h" +obelisk::Obelisk::Obelisk(std::string filename) +{ + kb_ = std::unique_ptr { + new obelisk::KnowledgeBase(filename.c_str())}; +} + std::string obelisk::Obelisk::getVersion() { return obelisk::version; @@ -15,10 +21,18 @@ double obelisk::Obelisk::query(const std::string& leftEntity, const std::string& verb, const std::string& rightEntity) { + obelisk::Fact fact = obelisk::Fact(obelisk::Entity(leftEntity), + obelisk::Entity(rightEntity), + obelisk::Verb(verb)); + + kb_->queryFact(fact); + + return fact.getIsTrue(); } std::string obelisk::Obelisk::query_action(const std::string& leftEntity, const std::string& verb, const std::string& rightEntity) { + return "yes"; } diff --git a/src/lib/obelisk_wrapper.cpp b/src/lib/obelisk_wrapper.cpp index 625609b..2498e90 100644 --- a/src/lib/obelisk_wrapper.cpp +++ b/src/lib/obelisk_wrapper.cpp @@ -5,9 +5,9 @@ extern "C" { - CObelisk* create_obelisk() + CObelisk* create_obelisk(const char* filename) { - obelisk::Obelisk* obelisk = new obelisk::Obelisk(); + obelisk::Obelisk* obelisk = new obelisk::Obelisk(filename); return reinterpret_cast(obelisk); } @@ -26,6 +26,18 @@ extern "C" return obelisk->getLibVersion(); } + double call_obelisk_query(CObelisk* p_obelisk, + const char* left_entity, + const char* verb, + const char* right_entity) + { + obelisk::Obelisk* obelisk + = reinterpret_cast(p_obelisk); + return obelisk->query(std::string(left_entity), + std::string(verb), + std::string(right_entity)); + } + void destroy_obelisk(CObelisk* p_obelisk) { obelisk::Obelisk* obelisk diff --git a/src/lib/obelisk_wrapper.h b/src/lib/obelisk_wrapper.h index c697816..732a31f 100644 --- a/src/lib/obelisk_wrapper.h +++ b/src/lib/obelisk_wrapper.h @@ -11,9 +11,10 @@ extern "C" /** * @brief Create a obelisk object. * + * @param[in] filename The name of the obelisk KnowledgeBase file to use. * @return CObelisk* Returns the obelisk object. */ - CObelisk *create_obelisk(); + CObelisk *create_obelisk(const char *filename); /** * @brief Calls the obelisk method getVersion. @@ -32,6 +33,20 @@ extern "C" */ int call_obelisk_getLibVersion(CObelisk *p_obelisk); + /** + * @brief Calls the obelisk method query. + * + * @param[in] p_obelisk The obelisk object pointer. + * @param[in] left_entity The left entity. + * @param[in] verb The verb. + * @param[in] right_entity The right entity. + * @return double Returns whether or not the Fact is true. + */ + double call_obelisk_query(CObelisk *p_obelisk, + const char *left_entity, + const char *verb, + const char *right_entity); + /** * @brief Delete a obelisk object. * diff --git a/src/parser.cpp b/src/parser.cpp index 907fa6b..952cdc0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -806,9 +806,9 @@ void obelisk::Parser::handleRule(std::unique_ptr& kb) insertFact(kb, rule.getReason()); // The rule is true, so the fact must be true to. - if (rule.getReason().getIsTrue()) + if (rule.getReason().getIsTrue() > 0) { - rule.getFact().setIsTrue(true); + rule.getFact().setIsTrue(1.0); } insertEntity(kb, rule.getFact().getLeftEntity()); @@ -957,7 +957,7 @@ void obelisk::Parser::insertFact(std::unique_ptr& kb, { if (updateIsTrue) { - fact.setIsTrue(true); + fact.setIsTrue(1.0); kb->updateIsTrue(fact); } } From d6532715bd15cad1541faf350cb48879636b8c24 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 23 Feb 2023 01:06:29 -0300 Subject: [PATCH 26/27] fix typo --- src/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.cpp b/src/parser.cpp index 952cdc0..e5657db 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -542,7 +542,7 @@ void obelisk::Parser::parseRule(obelisk::Rule& rule) if (rightEntity == "") { - throw obelisk::ParserException("missing left entity"); + throw obelisk::ParserException("missing right entity"); } if (verb == "") From 3a2312174344929951bd65799f5aeb235e4ad6b3 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 23 Feb 2023 01:07:44 -0300 Subject: [PATCH 27/27] add query for suggested actions --- src/lib/include/obelisk.h | 2 +- src/lib/include/obelisk_c.h | 15 +++++++++ src/lib/knowledge_base.cpp | 6 ++++ src/lib/knowledge_base.h | 16 +++++++++ src/lib/models/fact.cpp | 66 +++++++++++++++++++++++++++++++++++++ src/lib/models/fact.h | 11 +++++++ src/lib/obelisk.c | 8 +++++ src/lib/obelisk.cpp | 13 ++++++-- src/lib/obelisk_wrapper.cpp | 14 ++++++++ src/lib/obelisk_wrapper.h | 17 +++++++++- 10 files changed, 164 insertions(+), 4 deletions(-) diff --git a/src/lib/include/obelisk.h b/src/lib/include/obelisk.h index b078614..1e434bc 100644 --- a/src/lib/include/obelisk.h +++ b/src/lib/include/obelisk.h @@ -73,7 +73,7 @@ namespace obelisk * @param[in] rightEntity The right entity. * @return std::string Returns the suggested action. */ - std::string query_action(const std::string& leftEntity, + std::string queryAction(const std::string& leftEntity, const std::string& verb, const std::string& rightEntity); }; diff --git a/src/lib/include/obelisk_c.h b/src/lib/include/obelisk_c.h index fa5ab83..d1f0294 100644 --- a/src/lib/include/obelisk_c.h +++ b/src/lib/include/obelisk_c.h @@ -50,6 +50,21 @@ extern "C" const char* verb, const char* right_entity); + /** + * @brief Query the obelisk KnowledgeBase to get a suggested Action to do. + * + * @param[in] obelisk The obelisk object. + * @param[in] left_entity The left entity. + * @param[in] verb The verb. + * @param[in] right_entity The right entity. + * @return char* Returns the Action to do or an empty string if there is no + * action. + */ + extern char* obelisk_query_action(CObelisk* obelisk, + const char* left_entity, + const char* verb, + const char* right_entity); + /** * @brief Get the obelisk library so version. * diff --git a/src/lib/knowledge_base.cpp b/src/lib/knowledge_base.cpp index ec345df..36e250e 100644 --- a/src/lib/knowledge_base.cpp +++ b/src/lib/knowledge_base.cpp @@ -264,6 +264,12 @@ void obelisk::KnowledgeBase::queryFact(obelisk::Fact& fact) fact.selectByName(dbConnection_); } +void obelisk::KnowledgeBase::querySuggestAction(obelisk::Fact& fact, + obelisk::Action& action) +{ + fact.selectActionByFact(dbConnection_, action); +} + void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) diff --git a/src/lib/knowledge_base.h b/src/lib/knowledge_base.h index 24ae8bf..4930033 100644 --- a/src/lib/knowledge_base.h +++ b/src/lib/knowledge_base.h @@ -200,8 +200,24 @@ namespace obelisk */ void updateIsTrue(obelisk::Fact& fact); + /** + * @brief Query the KnowledgeBase to see if a Fact is true or false. + * + * @param[in] fact The Fact to check. + */ void queryFact(obelisk::Fact& fact); + /** + * @brief Query the KnowledgeBase to get a suggested action based + * on a Fact. + * If a SuggestAction doesn't exist, it will return an empty Action. + * + * @param[in] fact The Fact to search for. + * @param[out] action The Action that is suggested to take. + */ + void querySuggestAction(obelisk::Fact& fact, + obelisk::Action& action); + /** * @brief Take a float and divide it into 2 floats. * diff --git a/src/lib/models/fact.cpp b/src/lib/models/fact.cpp index 5bfc1ca..874e0cd 100644 --- a/src/lib/models/fact.cpp +++ b/src/lib/models/fact.cpp @@ -276,6 +276,72 @@ void obelisk::Fact::selectByName(sqlite3* dbConnection) } } +void obelisk::Fact::selectActionByFact(sqlite3* dbConnection, + obelisk::Action& action) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, + "SELECT CASE f.is_true WHEN 0 THEN (SELECT name FROM action WHERE id = fa.id) WHEN 1 THEN (SELECT name from action WHERE id = ta.id) END action FROM suggest_action LEFT JOIN action ta ON ta.id = suggest_action.true_action LEFT JOIN action fa ON fa.id = suggest_action.false_action LEFT JOIN fact f ON f.id = suggest_action.fact WHERE (f.id = ?)", + -1, + &ppStmt, + nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result = sqlite3_bind_int(ppStmt, 1, 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 : + action.setName((char*) sqlite3_column_text(ppStmt, 0)); + 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::Fact::insert(sqlite3* dbConnection) { if (dbConnection == nullptr) diff --git a/src/lib/models/fact.h b/src/lib/models/fact.h index f920ef4..21810b2 100644 --- a/src/lib/models/fact.h +++ b/src/lib/models/fact.h @@ -1,6 +1,7 @@ #ifndef OBELISK_MODELS_FACT_H #define OBELISK_MODELS_FACT_H +#include "models/action.h" #include "models/entity.h" #include "models/fact.h" #include "models/verb.h" @@ -216,6 +217,16 @@ namespace obelisk */ void selectByName(sqlite3* dbConnection); + /** + * @brief Select an Action from the KnowledgeBase using the provided + * Fact. + * + * @param[in] dbConnection The database connection to use. + * @param[out] action The Action to take based on the provided fact. + */ + void selectActionByFact(sqlite3* dbConnection, + obelisk::Action& action); + /** * @brief Insert the Fact into the KnowledgeBase. * diff --git a/src/lib/obelisk.c b/src/lib/obelisk.c index 9de3ded..3d41055 100644 --- a/src/lib/obelisk.c +++ b/src/lib/obelisk.c @@ -31,3 +31,11 @@ double obelisk_query(CObelisk* obelisk, { return call_obelisk_query(obelisk, left_entity, verb, right_entity); } + +char* obelisk_query_action(CObelisk* obelisk, + const char* left_entity, + const char* verb, + const char* right_entity) +{ + return call_obelisk_queryAction(obelisk, left_entity, verb, right_entity); +} diff --git a/src/lib/obelisk.cpp b/src/lib/obelisk.cpp index 289e22d..e2bb414 100644 --- a/src/lib/obelisk.cpp +++ b/src/lib/obelisk.cpp @@ -30,9 +30,18 @@ double obelisk::Obelisk::query(const std::string& leftEntity, return fact.getIsTrue(); } -std::string obelisk::Obelisk::query_action(const std::string& leftEntity, +std::string obelisk::Obelisk::queryAction(const std::string& leftEntity, const std::string& verb, const std::string& rightEntity) { - return "yes"; + obelisk::Fact fact = obelisk::Fact(obelisk::Entity(leftEntity), + obelisk::Entity(rightEntity), + obelisk::Verb(verb)); + + kb_->queryFact(fact); + + obelisk::Action action; + kb_->querySuggestAction(fact, action); + + return action.getName(); } diff --git a/src/lib/obelisk_wrapper.cpp b/src/lib/obelisk_wrapper.cpp index 2498e90..be9b13a 100644 --- a/src/lib/obelisk_wrapper.cpp +++ b/src/lib/obelisk_wrapper.cpp @@ -38,6 +38,20 @@ extern "C" std::string(right_entity)); } + char* call_obelisk_queryAction(CObelisk* p_obelisk, + const char* left_entity, + const char* verb, + const char* right_entity) + { + obelisk::Obelisk* obelisk + = reinterpret_cast(p_obelisk); + auto temp = obelisk->queryAction(std::string(left_entity), + std::string(verb), + std::string(right_entity)); + auto action = strdup(temp.c_str()); + return action; + } + void destroy_obelisk(CObelisk* p_obelisk) { obelisk::Obelisk* obelisk diff --git a/src/lib/obelisk_wrapper.h b/src/lib/obelisk_wrapper.h index 732a31f..b8c4b6d 100644 --- a/src/lib/obelisk_wrapper.h +++ b/src/lib/obelisk_wrapper.h @@ -20,7 +20,7 @@ extern "C" * @brief Calls the obelisk method getVersion. * * @param[in] p_obelisk The obelisk object pointer. - * @return const char* Returns the version. This must be freed by the + * @return char* Returns the version. This must be freed by the * caller. */ char *call_obelisk_getVersion(CObelisk *p_obelisk); @@ -47,6 +47,21 @@ extern "C" const char *verb, const char *right_entity); + /** + * @brief Calls the obelisk method queryAction. + * + * @param[in] p_obelisk The obelisk object pointer. + * @param[in] left_entity The left entity. + * @param[in] verb The verb. + * @param[in] right_entity The right entity. + * @return char* Returns the sugggested action to take or an empty string if + * no action is found. This must be freed by the caller. + */ + char *call_obelisk_queryAction(CObelisk *p_obelisk, + const char *left_entity, + const char *verb, + const char *right_entity); + /** * @brief Delete a obelisk object. *