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/doc/doxygen.conf.in b/doc/doxygen.conf.in index 684e675..2d007fc 100644 --- a/doc/doxygen.conf.in +++ b/doc/doxygen.conf.in @@ -2237,7 +2237,9 @@ SEARCH_INCLUDES = YES INCLUDE_PATH = @TOP_SRCDIR@/src \ @TOP_SRCDIR@/src/ast \ - @TOP_SRCDIR@/src/models + @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 diff --git a/meson.build b/meson.build index b8b534f..e27febf 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,5 @@ project('obelisk', + 'c', 'cpp', version : '1.0.0', license : 'BSD-3-Clause', @@ -9,6 +10,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() 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..55c5800 100644 --- a/src/ast/call_expression_ast.h +++ b/src/ast/call_expression_ast.h @@ -9,25 +9,74 @@ 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: - CallExpressionAST(const std::string &callee, std::vector> args) : + /** + * @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..78d1770 100644 --- a/src/ast/error.h +++ b/src/ast/error.h @@ -7,8 +7,22 @@ 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..d80c378 100644 --- a/src/ast/expression_ast.h +++ b/src/ast/expression_ast.h @@ -5,10 +5,25 @@ 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.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 d9f433f..8f3e0ac 100644 --- a/src/ast/function_ast.h +++ b/src/ast/function_ast.h @@ -8,22 +8,58 @@ 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: - FunctionAST(std::unique_ptr prototype, std::unique_ptr body) : + /** + * @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.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 f8f22a1..ec1e7bc 100644 --- a/src/ast/prototype_ast.h +++ b/src/ast/prototype_ast.h @@ -8,30 +8,76 @@ 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: - PrototypeAST(const std::string& name, std::vector args) : + /** + * @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/knowledge_base.cpp b/src/knowledge_base.cpp deleted file mode 100644 index f9e3ae9..0000000 --- a/src/knowledge_base.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#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 -#include -#include - -obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags) -{ - filename_ = std::move(filename); - flags_ = std::move(flags); - - std::filesystem::path path {filename}; - auto dbExists = std::filesystem::exists(path); - - auto result = sqlite3_open_v2(filename, &dbConnection_, flags, NULL); - if (result != SQLITE_OK) - { - throw new KnowledgeBaseException("database could not be opened"); - } - - enableForeignKeys(); - - if (!dbExists) - { - createTable(obelisk::Action::createTable); - createTable(obelisk::Entity::createTable); - createTable(obelisk::Verb::createTable); - createTable(obelisk::Fact::createTable); - createTable(obelisk::Rule::createTable); - createTable(obelisk::SuggestAction::createTable); - } -} - -obelisk::KnowledgeBase::~KnowledgeBase() -{ - if (dbConnection_) - { - sqlite3_close_v2(dbConnection_); - } -} - -void obelisk::KnowledgeBase::enableForeignKeys() -{ - char* errmsg; - int result = sqlite3_exec(dbConnection_, "PRAGMA foreign_keys = ON;", NULL, NULL, &errmsg); - if (result != SQLITE_OK) - { - if (errmsg) - { - throw obelisk::KnowledgeBaseException(errmsg); - } - else - { - throw obelisk::KnowledgeBaseException(); - } - } -} - -void obelisk::KnowledgeBase::createTable(std::function function) -{ - char* errmsg; - int result = sqlite3_exec(dbConnection_, function(), NULL, NULL, &errmsg); - if (result != SQLITE_OK) - { - if (errmsg) - { - throw obelisk::KnowledgeBaseException(errmsg); - } - else - { - throw obelisk::KnowledgeBaseException(); - } - } -} - -void obelisk::KnowledgeBase::addEntities(std::vector& entities) -{ - for (auto& entity : entities) - { - try - { - entity.insertEntity(dbConnection_); - } - catch (obelisk::DatabaseConstraintException& exception) - { - // ignore unique constraint error - if (std::strcmp(exception.what(), "UNIQUE constraint failed: entity.name") != 0) - { - throw; - } - } - } -} - -void obelisk::KnowledgeBase::addVerbs(std::vector& verbs) -{ - for (auto& verb : verbs) - { - try - { - verb.insertVerb(dbConnection_); - } - catch (obelisk::DatabaseConstraintException& exception) - { - // ignore unique constraint error - if (std::strcmp(exception.what(), "UNIQUE constraint failed: verb.name") != 0) - { - throw; - } - } - } -} - -void obelisk::KnowledgeBase::addFacts(std::vector& facts) -{ - for (auto& fact : facts) - { - try - { - fact.insertFact(dbConnection_); - } - catch (obelisk::DatabaseConstraintException& exception) - { - // ignore unique constraint error - if (std::strcmp(exception.what(), - "UNIQUE constraint failed: fact.left_entity, fact.right_entity, fact.verb") - != 0) - { - throw; - } - } - } -} - -void obelisk::KnowledgeBase::getEntity(obelisk::Entity& entity) -{ - entity.selectEntity(dbConnection_); -} - -void obelisk::KnowledgeBase::getVerb(obelisk::Verb& verb) -{ - verb.selectVerb(dbConnection_); -} - -void obelisk::KnowledgeBase::getFact(obelisk::Fact& fact) -{ - fact.selectFact(dbConnection_); -} - -void obelisk::KnowledgeBase::getFloat(float& result1, float& result2, double var) -{ - result1 = (float) var; - result2 = (float) (var - (double) result1); -} - -void obelisk::KnowledgeBase::getDouble(double& result, float var1, float var2) -{ - result = (double) ((double) var2 + (double) var1); -} diff --git a/src/lexer.cpp b/src/lexer.cpp index 32509b1..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); } } @@ -40,7 +41,7 @@ int obelisk::Lexer::getToken() if (getIdentifier() == "rule") { - return Token::kTokenFact; + return Token::kTokenRule; } if (getIdentifier() == "action") diff --git a/src/lib/include/obelisk.h b/src/lib/include/obelisk.h new file mode 100644 index 0000000..1e434bc --- /dev/null +++ b/src/lib/include/obelisk.h @@ -0,0 +1,82 @@ +#ifndef OBELISK_INCLUDE_OBELISK_H +#define OBELISK_INCLUDE_OBELISK_H + +#include "knowledge_base.h" + +#include +#include + +/** + * @brief The obelisk namespace contains everything needed to compile obelisk + * code. + * + */ +namespace obelisk +{ + /** + * @brief The obelisk library provides everything needed to consult the + * KnowledgeBase. + * + */ + class Obelisk + { + private: + std::unique_ptr kb_; + + public: + /** + * @brief Construct a new Obelisk object. + * + */ + Obelisk(std::string filename); + + /** + * @brief Destroy the Obelisk object. + * + */ + ~Obelisk() = default; + + /** + * @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(); + + /** + * @brief Query the obelisk KnowledgeBase to see if a Fact is true + * or not. + * + * @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 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 queryAction(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..d1f0294 --- /dev/null +++ b/src/lib/include/obelisk_c.h @@ -0,0 +1,80 @@ +#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. + * + * @param[in] filename The obelisk KnowledgeBase file to use. + * @return CObelisk* Returns an obelisk object. + */ + extern CObelisk* obelisk_open(const char* filename); + + /** + * @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 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 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. + * + * @param[in] obelisk The obelisk object. + * @return int Returns the so version. + */ + extern int obelisk_get_lib_version(CObelisk* obelisk); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/lib/knowledge_base.cpp b/src/lib/knowledge_base.cpp new file mode 100644 index 0000000..36e250e --- /dev/null +++ b/src/lib/knowledge_base.cpp @@ -0,0 +1,284 @@ +#include "knowledge_base.h" +#include "models/error.h" + +#include +#include +#include +#include + +obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags) +{ + filename_ = std::move(filename); + flags_ = std::move(flags); + + std::filesystem::path path {filename}; + auto dbExists = std::filesystem::exists(path); + + auto result = sqlite3_open_v2(filename, &dbConnection_, flags, NULL); + if (result != SQLITE_OK) + { + throw new KnowledgeBaseException("database could not be opened"); + } + + enableForeignKeys(); + + if (!dbExists) + { + createTable(obelisk::Action::createTable); + createTable(obelisk::Entity::createTable); + createTable(obelisk::Verb::createTable); + createTable(obelisk::Fact::createTable); + createTable(obelisk::Rule::createTable); + createTable(obelisk::SuggestAction::createTable); + } +} + +obelisk::KnowledgeBase::~KnowledgeBase() +{ + if (dbConnection_) + { + sqlite3_close_v2(dbConnection_); + } +} + +void obelisk::KnowledgeBase::enableForeignKeys() +{ + char* errmsg; + int result = sqlite3_exec(dbConnection_, + "PRAGMA foreign_keys = ON;", + NULL, + NULL, + &errmsg); + if (result != SQLITE_OK) + { + if (errmsg) + { + throw obelisk::KnowledgeBaseException(errmsg); + } + else + { + throw obelisk::KnowledgeBaseException(); + } + } +} + +void obelisk::KnowledgeBase::createTable(std::function function) +{ + char* errmsg; + int result = sqlite3_exec(dbConnection_, function(), NULL, NULL, &errmsg); + if (result != SQLITE_OK) + { + if (errmsg) + { + throw obelisk::KnowledgeBaseException(errmsg); + } + else + { + throw obelisk::KnowledgeBaseException(); + } + } +} + +void obelisk::KnowledgeBase::addEntities(std::vector& entities) +{ + for (auto& entity : entities) + { + try + { + entity.insert(dbConnection_); + } + catch (obelisk::DatabaseConstraintException& exception) + { + // ignore unique constraint error + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: entity.name") + != 0) + { + throw; + } + } + } +} + +void obelisk::KnowledgeBase::addVerbs(std::vector& verbs) +{ + for (auto& verb : verbs) + { + try + { + verb.insert(dbConnection_); + } + catch (obelisk::DatabaseConstraintException& exception) + { + // ignore unique constraint error + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: verb.name") + != 0) + { + throw; + } + } + } +} + +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) + { + try + { + fact.insert(dbConnection_); + } + catch (obelisk::DatabaseConstraintException& exception) + { + // ignore unique constraint error + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: fact.left_entity, fact.right_entity, fact.verb") + != 0) + { + throw; + } + } + } +} + +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::addRules(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_); +} + +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.selectById(dbConnection_); +} + +void obelisk::KnowledgeBase::getSuggestAction( + obelisk::SuggestAction& suggestAction) +{ + suggestAction.selectById(dbConnection_); +} + +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() > 0) + { + auto updateFact = rule.getFact(); + updateFact.setIsTrue(1.0); + updateFact.updateIsTrue(dbConnection_); + } + } +} + +void obelisk::KnowledgeBase::updateIsTrue(obelisk::Fact& fact) +{ + fact.updateIsTrue(dbConnection_); +} + +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) +{ + result1 = (float) var; + result2 = (float) (var - (double) result1); +} + +void obelisk::KnowledgeBase::getDouble(double& result, float var1, float var2) +{ + result = (double) ((double) var2 + (double) var1); +} diff --git a/src/knowledge_base.h b/src/lib/knowledge_base.h similarity index 58% rename from src/knowledge_base.h rename to src/lib/knowledge_base.h index 05cced7..4930033 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); @@ -72,19 +75,20 @@ namespace obelisk * base as. */ KnowledgeBase(const char* filename) : - KnowledgeBase(filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) + KnowledgeBase(filename, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) { } /** * @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,7 +96,7 @@ 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. @@ -100,7 +104,15 @@ namespace obelisk void addVerbs(std::vector& verbs); /** - * @brief Add facts to the database. + * @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 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. @@ -108,7 +120,25 @@ 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 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. * * @param[in,out] entity The Entity object should contain just the * ID and the rest will be filled in. @@ -116,7 +146,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. @@ -124,20 +154,76 @@ namespace obelisk void getVerb(obelisk::Verb& verb); /** - * @brief Get a fact object based on the ID it contains. + * @brief Get an Action based on the ID it contains. * - * @param[in,out] fact The fact object should contain just the ID + * @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. */ 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 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 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 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. * * 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. + * 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. diff --git a/src/lib/meson.build b/src/lib/meson.build new file mode 100644 index 0000000..70f39d5 --- /dev/null +++ b/src/lib/meson.build @@ -0,0 +1,36 @@ +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('models') + +obelisk_lib_sources = files( + 'obelisk.cpp', + 'obelisk.c', + 'obelisk_wrapper.cpp', + 'knowledge_base.cpp' +) + +obelisk_lib_sources += obelisk_model_sources + +sqlite3 = dependency('sqlite3') + +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 = declare_dependency( + include_directories : incdirs, + link_with : lib, + sources : obelisk_lib_sources +) diff --git a/src/lib/models/action.cpp b/src/lib/models/action.cpp new file mode 100644 index 0000000..8f8c880 --- /dev/null +++ b/src/lib/models/action.cpp @@ -0,0 +1,168 @@ +#include "models/action.h" +#include "models/error.h" + +const char* obelisk::Action::createTable() +{ + return R"( + CREATE TABLE "action" ( + "id" INTEGER NOT NULL UNIQUE, + "name" TEXT NOT NULL CHECK(trim(name) != '') UNIQUE, + PRIMARY KEY("id" AUTOINCREMENT) + ); + )"; +} + +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_; +} + +void obelisk::Action::setId(int id) +{ + id_ = id; +} + +std::string& obelisk::Action::getName() +{ + return name_; +} + +void obelisk::Action::setName(std::string name) +{ + name_ = name; +} diff --git a/src/models/action.h b/src/lib/models/action.h similarity index 78% rename from src/models/action.h rename to src/lib/models/action.h index 7f22827..2d06c03 100644 --- a/src/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/models/entity.cpp b/src/lib/models/entity.cpp similarity index 85% rename from src/models/entity.cpp rename to src/lib/models/entity.cpp index f051372..d90da0a 100644 --- a/src/models/entity.cpp +++ b/src/lib/models/entity.cpp @@ -12,7 +12,7 @@ const char* obelisk::Entity::createTable() )"; } -void obelisk::Entity::selectEntity(sqlite3* dbConnection) +void obelisk::Entity::selectByName(sqlite3* dbConnection) { if (dbConnection == nullptr) { @@ -21,7 +21,11 @@ void obelisk::Entity::selectEntity(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) { @@ -76,7 +80,7 @@ void obelisk::Entity::selectEntity(sqlite3* dbConnection) } } -void obelisk::Entity::insertEntity(sqlite3* dbConnection) +void obelisk::Entity::insert(sqlite3* dbConnection) { if (dbConnection == nullptr) { @@ -85,13 +89,18 @@ void obelisk::Entity::insertEntity(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::insertEntity(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/models/entity.h b/src/lib/models/entity.h similarity index 86% rename from src/models/entity.h rename to src/lib/models/entity.h index 7ede503..0241575 100644 --- a/src/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,20 +108,20 @@ namespace obelisk void setName(std::string name); /** - * @brief Select an Entity from the database based on the object's - * ID. + * @brief Select an Entity from the KnowledgeBase based on the + * object's name. * * @param[in] dbConnection The database connection to use. */ - void selectEntity(sqlite3* dbConnection); + void selectByName(sqlite3* dbConnection); /** - * @brief Insert an Entity into the database 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. */ - void insertEntity(sqlite3* dbConnection); + void insert(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/lib/models/error.h b/src/lib/models/error.h new file mode 100644 index 0000000..ba28bd7 --- /dev/null +++ b/src/lib/models/error.h @@ -0,0 +1,193 @@ +#ifndef OBELISK_MODELS_ERROR_H +#define OBELISK_MODELS_ERROR_H + +#include +#include + +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() + + { + setErrorMessage("misuse of the database routine"); + } + }; + + /** + * @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); + } + }; +} // namespace obelisk + +#endif diff --git a/src/lib/models/fact.cpp b/src/lib/models/fact.cpp new file mode 100644 index 0000000..874e0cd --- /dev/null +++ b/src/lib/models/fact.cpp @@ -0,0 +1,600 @@ +#include "models/error.h" +#include "models/fact.h" + +const char* obelisk::Fact::createTable() +{ + return R"( + CREATE TABLE "fact" ( + "id" INTEGER NOT NULL UNIQUE, + "left_entity" INTEGER NOT NULL, + "verb" INTEGER NOT NULL, + "right_entity" INTEGER NOT NULL, + "is_true" INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY("id" AUTOINCREMENT), + UNIQUE("left_entity", "right_entity", "verb") + FOREIGN KEY("verb") REFERENCES "verb"("id") ON DELETE RESTRICT, + FOREIGN KEY("right_entity") REFERENCES "entity"("id") ON DELETE RESTRICT, + FOREIGN KEY("left_entity") REFERENCES "entity"("id") ON DELETE RESTRICT + ); + )"; +} + +void obelisk::Fact::selectById(sqlite3* dbConnection) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* 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)); + } + + if (getId() == 0) + { + 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, 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 + { + 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 : + 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::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::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) + { + throw obelisk::DatabaseException("database isn't open"); + } + + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, + "INSERT INTO fact (left_entity, right_entity, verb, is_true) VALUES (?, ?, ?, ?)", + -1, + &ppStmt, + nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + 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, 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; + } + + 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) + { + 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)); + } +} + +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_; +} + +void obelisk::Fact::setId(int id) +{ + id_ = id; +} + +obelisk::Entity& obelisk::Fact::getLeftEntity() +{ + return leftEntity_; +} + +void obelisk::Fact::setLeftEntity(obelisk::Entity leftEntity) +{ + leftEntity_ = leftEntity; +} + +obelisk::Entity& obelisk::Fact::getRightEntity() +{ + return rightEntity_; +} + +void obelisk::Fact::setRightEntity(obelisk::Entity rightEntity) +{ + rightEntity_ = rightEntity; +} + +obelisk::Verb& obelisk::Fact::getVerb() +{ + return verb_; +} + +void obelisk::Fact::setVerb(obelisk::Verb verb) +{ + verb_ = verb; +} + +double& obelisk::Fact::getIsTrue() +{ + return isTrue_; +} + +void obelisk::Fact::setIsTrue(double isTrue) +{ + isTrue_ = isTrue; +} diff --git a/src/lib/models/fact.h b/src/lib/models/fact.h new file mode 100644 index 0000000..21810b2 --- /dev/null +++ b/src/lib/models/fact.h @@ -0,0 +1,247 @@ +#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" + +#include + +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. + * + */ + double isTrue_; + + public: + /** + * @brief Construct a new Fact object. + * + */ + Fact() : + id_(0), + leftEntity_(), + rightEntity_(), + verb_(), + isTrue_(0) + { + } + + /** + * @brief Construct a new Fact object. + * + * @param[in] id The ID of the Fact in the KnowledgeBase. + */ + Fact(int id) : + id_(id), + leftEntity_(), + rightEntity_(), + verb_(), + isTrue_(0) + { + } + + /** + * @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, + double isTrue = 0) : + id_(0), + leftEntity_(leftEntity), + rightEntity_(rightEntity), + verb_(verb), + isTrue_(isTrue) + { + } + + /** + * @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, + obelisk::Verb verb, + double isTrue = 0) : + id_(id), + leftEntity_(leftEntity), + rightEntity_(rightEntity), + verb_(verb), + isTrue_(isTrue) + { + } + + /** + * @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(); + + /** + * @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. + */ + double& getIsTrue(); + + /** + * @brief Set the Fact as true or false. + * + * @param[in] isTrue Whether or not the Fact is true. + */ + void setIsTrue(double isTrue); + + /** + * @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 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 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. + * + * @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 + +#endif 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/lib/models/rule.cpp b/src/lib/models/rule.cpp new file mode 100644 index 0000000..fb2722c --- /dev/null +++ b/src/lib/models/rule.cpp @@ -0,0 +1,286 @@ +#include "models/error.h" +#include "models/rule.h" + +const char* obelisk::Rule::createTable() +{ + return R"( + CREATE TABLE "rule" ( + "id" INTEGER NOT NULL UNIQUE, + "fact" INTEGER NOT NULL, + "reason" INTEGER NOT NULL CHECK("reason" != "fact"), + PRIMARY KEY("id" AUTOINCREMENT), + UNIQUE("fact", "reason"), + FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT, + FOREIGN KEY("reason") REFERENCES "fact"("id") ON DELETE RESTRICT + ); + )"; +} + +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)); + } +} + +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_; +} + +void obelisk::Rule::setId(int id) +{ + id_ = id; +} + +obelisk::Fact& obelisk::Rule::getFact() +{ + return fact_; +} + +void obelisk::Rule::setFact(obelisk::Fact fact) +{ + fact_ = fact; +} + +obelisk::Fact& obelisk::Rule::getReason() +{ + return reason_; +} + +void obelisk::Rule::setReason(obelisk::Fact reason) +{ + reason_ = reason; +} diff --git a/src/lib/models/rule.h b/src/lib/models/rule.h new file mode 100644 index 0000000..efbdced --- /dev/null +++ b/src/lib/models/rule.h @@ -0,0 +1,163 @@ +#ifndef OBELISK_MODELS_RULE_H +#define OBELISK_MODELS_RULE_H + +#include "models/fact.h" + +#include +#include + +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_(), + reason_() + { + } + + /** + * @brief Construct a new Rule object. + * + * @param[in] id The ID of the Rule in the KnowledgeBase. + */ + Rule(int id) : + id_(id), + fact_(), + reason_() + { + } + + /** + * @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), + reason_(reason) + { + } + + /** + * @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), + reason_(reason) + { + } + + /** + * @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); + + /** + * @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 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. + * + * @param[in] dbConnection The database connection to use. + */ + void insert(sqlite3* dbConnection); + }; +} // namespace obelisk + +#endif diff --git a/src/models/fact.cpp b/src/lib/models/suggest_action.cpp similarity index 71% rename from src/models/fact.cpp rename to src/lib/models/suggest_action.cpp index 8011bcc..4226079 100644 --- a/src/models/fact.cpp +++ b/src/lib/models/suggest_action.cpp @@ -1,25 +1,24 @@ #include "models/error.h" -#include "models/fact.h" +#include "models/suggest_action.h" -const char* obelisk::Fact::createTable() +const char* obelisk::SuggestAction::createTable() { return R"( - CREATE TABLE "fact" ( + CREATE TABLE "suggest_action" ( "id" INTEGER NOT NULL UNIQUE, - "left_entity" INTEGER NOT NULL, - "verb" INTEGER NOT NULL, - "right_entity" INTEGER NOT NULL, - "is_true" INTEGER NOT NULL DEFAULT 0, + "fact" INTEGER NOT NULL, + "true_action" INTEGER NOT NULL, + "false_action" INTEGER NOT NULL, PRIMARY KEY("id" AUTOINCREMENT), - UNIQUE("left_entity", "right_entity", "verb") - FOREIGN KEY("verb") REFERENCES "verb"("id") ON DELETE RESTRICT, - FOREIGN KEY("right_entity") REFERENCES "entity"("id") ON DELETE RESTRICT, - FOREIGN KEY("left_entity") REFERENCES "entity"("id") ON DELETE RESTRICT + UNIQUE("fact", "true_action", "false_action"), + FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT, + FOREIGN KEY("true_action") REFERENCES "action"("id") ON DELETE RESTRICT, + FOREIGN KEY("false_action") REFERENCES "action"("id") ON DELETE RESTRICT ); )"; } -void obelisk::Fact::selectFact(sqlite3* dbConnection) +void obelisk::SuggestAction::selectById(sqlite3* dbConnection) { if (dbConnection == nullptr) { @@ -29,7 +28,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, fact, true_action, false_action FROM suggest_action WHERE (fact=? AND true_action=? AND false_action=?)", -1, &ppStmt, nullptr); @@ -38,7 +37,7 @@ void obelisk::Fact::selectFact(sqlite3* dbConnection) throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); } - result = sqlite3_bind_int(ppStmt, 1, getLeftEntity().getId()); + result = sqlite3_bind_int(ppStmt, 1, getFact().getId()); switch (result) { case SQLITE_OK : @@ -57,7 +56,7 @@ void obelisk::Fact::selectFact(sqlite3* dbConnection) break; } - result = sqlite3_bind_int(ppStmt, 2, getRightEntity().getId()); + result = sqlite3_bind_int(ppStmt, 2, getTrueAction().getId()); switch (result) { case SQLITE_OK : @@ -76,7 +75,7 @@ void obelisk::Fact::selectFact(sqlite3* dbConnection) break; } - result = sqlite3_bind_int(ppStmt, 3, getVerb().getId()); + result = sqlite3_bind_int(ppStmt, 3, getFalseAction().getId()); switch (result) { case SQLITE_OK : @@ -103,9 +102,9 @@ void obelisk::Fact::selectFact(sqlite3* dbConnection) 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)); + 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(); @@ -125,7 +124,7 @@ void obelisk::Fact::selectFact(sqlite3* dbConnection) } } -void obelisk::Fact::insertFact(sqlite3* dbConnection) +void obelisk::SuggestAction::insert(sqlite3* dbConnection) { if (dbConnection == nullptr) { @@ -135,7 +134,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 suggest_action (fact, true_action, false_action) VALUES (?, ?, ?)", -1, &ppStmt, nullptr); @@ -144,7 +143,7 @@ void obelisk::Fact::insertFact(sqlite3* dbConnection) throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); } - result = sqlite3_bind_int(ppStmt, 1, getLeftEntity().getId()); + result = sqlite3_bind_int(ppStmt, 1, getFact().getId()); switch (result) { case SQLITE_OK : @@ -163,7 +162,7 @@ void obelisk::Fact::insertFact(sqlite3* dbConnection) break; } - result = sqlite3_bind_int(ppStmt, 2, getRightEntity().getId()); + result = sqlite3_bind_int(ppStmt, 2, getTrueAction().getId()); switch (result) { case SQLITE_OK : @@ -182,7 +181,7 @@ void obelisk::Fact::insertFact(sqlite3* dbConnection) break; } - result = sqlite3_bind_int(ppStmt, 3, getVerb().getId()); + result = sqlite3_bind_int(ppStmt, 3, getFalseAction().getId()); switch (result) { case SQLITE_OK : @@ -209,7 +208,8 @@ void obelisk::Fact::insertFact(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; @@ -228,42 +228,42 @@ void obelisk::Fact::insertFact(sqlite3* dbConnection) } } -int& obelisk::Fact::getId() +int& obelisk::SuggestAction::getId() { return id_; } -void obelisk::Fact::setId(int id) +void obelisk::SuggestAction::setId(int id) { id_ = id; } -obelisk::Entity& obelisk::Fact::getLeftEntity() +obelisk::Fact& obelisk::SuggestAction::getFact() { - return leftEntity_; + return fact_; } -void obelisk::Fact::setLeftEntity(obelisk::Entity leftEntity) +void obelisk::SuggestAction::setFact(obelisk::Fact fact) { - leftEntity_ = leftEntity; + fact_ = fact; } -obelisk::Entity& obelisk::Fact::getRightEntity() +obelisk::Action& obelisk::SuggestAction::getTrueAction() { - return rightEntity_; + return trueAction_; } -void obelisk::Fact::setRightEntity(obelisk::Entity rightEntity) +void obelisk::SuggestAction::setTrueAction(obelisk::Action trueAction) { - rightEntity_ = rightEntity; + trueAction_ = trueAction; } -obelisk::Verb& obelisk::Fact::getVerb() +obelisk::Action& obelisk::SuggestAction::getFalseAction() { - return verb_; + return falseAction_; } -void obelisk::Fact::setVerb(obelisk::Verb verb) +void obelisk::SuggestAction::setFalseAction(obelisk::Action falseAction) { - verb_ = verb; + falseAction_ = falseAction; } diff --git a/src/lib/models/suggest_action.h b/src/lib/models/suggest_action.h new file mode 100644 index 0000000..16d123e --- /dev/null +++ b/src/lib/models/suggest_action.h @@ -0,0 +1,185 @@ +#ifndef OBELISK_MODELS_SUGGEST_ACTION_H +#define OBELISK_MODELS_SUGGEST_ACTION_H + +#include "models/action.h" +#include "models/fact.h" + +#include + +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_(), + trueAction_(), + falseAction_() + { + } + + /** + * @brief Construct a new SuggestAction object. + * + * @param[in] id The ID of the SuggestAction in the KnowledgeBase. + */ + SuggestAction(int id) : + id_(id), + fact_(), + trueAction_(), + falseAction_() + { + } + + /** + * @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), + trueAction_(trueAction), + falseAction_(falseAction) + { + } + + /** + * @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), + trueAction_(trueAction), + falseAction_(falseAction) + { + } + + /** + * @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 + +#endif diff --git a/src/models/verb.cpp b/src/lib/models/verb.cpp similarity index 86% rename from src/models/verb.cpp rename to src/lib/models/verb.cpp index e47d5cd..cd8c40b 100644 --- a/src/models/verb.cpp +++ b/src/lib/models/verb.cpp @@ -14,7 +14,7 @@ const char* obelisk::Verb::createTable() )"; } -void obelisk::Verb::selectVerb(sqlite3* dbConnection) +void obelisk::Verb::selectByName(sqlite3* dbConnection) { if (dbConnection == nullptr) { @@ -23,7 +23,11 @@ void obelisk::Verb::selectVerb(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)); @@ -76,7 +80,7 @@ void obelisk::Verb::selectVerb(sqlite3* dbConnection) } } -void obelisk::Verb::insertVerb(sqlite3* dbConnection) +void obelisk::Verb::insert(sqlite3* dbConnection) { if (dbConnection == nullptr) { @@ -85,13 +89,18 @@ void obelisk::Verb::insertVerb(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::insertVerb(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 new file mode 100644 index 0000000..150e86f --- /dev/null +++ b/src/lib/models/verb.h @@ -0,0 +1,126 @@ +#ifndef OBELISK_MODELS_VERB_H +#define OBELISK_MODELS_VERB_H + +#include + +#include + +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 KnowledgeBase. + * + */ + 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 selectByName(sqlite3* dbConnection); + + /** + * @brief Insert a new verb into the KnowledgeBase. + * + * @param[in] dbConnection The database connection to use. + */ + void insert(sqlite3* dbConnection); + }; +} // namespace obelisk + +#endif diff --git a/src/lib/obelisk.c b/src/lib/obelisk.c new file mode 100644 index 0000000..3d41055 --- /dev/null +++ b/src/lib/obelisk.c @@ -0,0 +1,41 @@ +#include "obelisk_c.h" +#include "obelisk_wrapper.h" + +#include +#include + +CObelisk* obelisk_open(const char* filename) +{ + return create_obelisk(filename); +} + +void obelisk_close(CObelisk* obelisk) +{ + destroy_obelisk(obelisk); +} + +char* obelisk_get_version(CObelisk* obelisk) +{ + return call_obelisk_getVersion(obelisk); +} + +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); +} + +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 new file mode 100644 index 0000000..e2bb414 --- /dev/null +++ b/src/lib/obelisk.cpp @@ -0,0 +1,47 @@ +#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; +} + +int obelisk::Obelisk::getLibVersion() +{ + return obelisk::soVersion; +} + +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::queryAction(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); + + obelisk::Action action; + kb_->querySuggestAction(fact, action); + + return action.getName(); +} diff --git a/src/lib/obelisk_wrapper.cpp b/src/lib/obelisk_wrapper.cpp new file mode 100644 index 0000000..be9b13a --- /dev/null +++ b/src/lib/obelisk_wrapper.cpp @@ -0,0 +1,61 @@ +#include "obelisk.h" +#include "obelisk_wrapper.h" + +#include + +extern "C" +{ + CObelisk* create_obelisk(const char* filename) + { + obelisk::Obelisk* obelisk = new obelisk::Obelisk(filename); + return reinterpret_cast(obelisk); + } + + char* call_obelisk_getVersion(CObelisk* p_obelisk) + { + obelisk::Obelisk* obelisk + = reinterpret_cast(p_obelisk); + auto version = strdup(obelisk->getVersion().c_str()); + return version; + } + + int call_obelisk_getLibVersion(CObelisk* p_obelisk) + { + obelisk::Obelisk* obelisk + = reinterpret_cast(p_obelisk); + 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)); + } + + 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 + = reinterpret_cast(p_obelisk); + delete obelisk; + } +}; diff --git a/src/lib/obelisk_wrapper.h b/src/lib/obelisk_wrapper.h new file mode 100644 index 0000000..b8c4b6d --- /dev/null +++ b/src/lib/obelisk_wrapper.h @@ -0,0 +1,76 @@ +#ifndef OBELISK_INCLUDE_OBELISK_WRAPPER_H +#define OBELISK_INCLUDE_OBELISK_WRAPPER_H + +#include "include/obelisk_c.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @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(const char *filename); + + /** + * @brief Calls the obelisk method getVersion. + * + * @param[in] p_obelisk The obelisk object pointer. + * @return char* Returns the version. This must be freed by the + * caller. + */ + 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 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 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. + * + * @param[in] p_obelisk The obelisk object pointer. + */ + void destroy_obelisk(CObelisk *p_obelisk); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/lib/version.h.in b/src/lib/version.h.in new file mode 100644 index 0000000..c9aee59 --- /dev/null +++ b/src/lib/version.h.in @@ -0,0 +1,21 @@ +#ifndef OBELISK_VERSION_H +#define OBELISK_VERSION_H + +namespace obelisk +{ + /** + * @brief The current version of obelisk. + * + */ + const std::string version = "@version@"; + + /** + * @brief The current library version of obelisk. + * + */ + // clang-format off + const int soVersion = @so_version@; + // clang-format on +} // namespace obelisk + +#endif diff --git a/src/obelisk.cpp b/src/main.cpp similarity index 70% rename from src/obelisk.cpp rename to src/main.cpp index 8d64044..3aa2422 100644 --- a/src/obelisk.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ #include "knowledge_base.h" #include "lexer.h" +#include "main.h" #include "obelisk.h" #include "parser.h" #include "version.h" @@ -9,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) { @@ -27,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) { @@ -52,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(); @@ -83,13 +89,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(); + try + { + parser->handleAction(kb); + } + catch (obelisk::ParserException& exception) + { + std::cout << "Error: " << exception.what() << std::endl; + return EXIT_FAILURE; + } break; default : parser->getNextToken(); @@ -113,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/obelisk.h b/src/main.h similarity index 87% rename from src/obelisk.h rename to src/main.h index e3abfcb..b330b53 100644 --- a/src/obelisk.h +++ b/src/main.h @@ -1,7 +1,10 @@ +#ifndef OBELISK_MAIN_H +#define OBELISK_MAIN_H + #include /** - * @brief The obelisk namespace contains everything needed to compile obelisk. + * @brief The obelisk namespace contains everything needed to compile obelisk * code. * */ @@ -38,9 +41,13 @@ 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. */ - int mainLoop(const std::vector &sourceFiles, const std::string &kbFile); + int mainLoop(const std::vector &sourceFiles, + const std::string &kbFile); } // namespace obelisk + +#endif diff --git a/src/meson.build b/src/meson.build index 81adf96..5d5b978 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,15 +1,9 @@ -conf_data = configuration_data() -conf_data.set('version', meson.project_version()) -configure_file(input : 'version.h.in', - output : 'version.h', - configuration : conf_data -) +subdir('lib') obelisk_sources = files( - 'obelisk.cpp', + 'main.cpp', 'lexer.cpp', - 'parser.cpp', - 'knowledge_base.cpp' + 'parser.cpp' ) sqlite3 = dependency('sqlite3') @@ -17,9 +11,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') @@ -28,7 +19,7 @@ link_args = ' ' + r.stdout().replace('\n', ' ') executable('obelisk', obelisk_sources, - dependencies : [sqlite3], + dependencies : [libobelisk, sqlite3], cpp_args : cpp_args.split(), link_args : link_args.split(), install : true diff --git a/src/models/action.cpp b/src/models/action.cpp deleted file mode 100644 index bb6ed06..0000000 --- a/src/models/action.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "models/action.h" - -const char* obelisk::Action::createTable() -{ - return R"( - CREATE TABLE "action" ( - "id" INTEGER NOT NULL UNIQUE, - "name" TEXT NOT NULL CHECK(trim(name) != '') UNIQUE, - PRIMARY KEY("id" AUTOINCREMENT) - ); - )"; -} - -int& obelisk::Action::getId() -{ - return id_; -} - -void obelisk::Action::setId(int id) -{ - id_ = id; -} - -std::string& obelisk::Action::getName() -{ - return name_; -} - -void obelisk::Action::setName(std::string name) -{ - name_ = name; -} diff --git a/src/models/error.h b/src/models/error.h deleted file mode 100644 index 1e1af7d..0000000 --- a/src/models/error.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef OBELISK_MODELS_ERROR_H -#define OBELISK_MODELS_ERROR_H - -#include -#include - -namespace obelisk -{ - class DatabaseException : public std::exception - { - protected: - std::string errorMessage_; - - public: - DatabaseException() : - errorMessage_("an unknown error ocurred") - { - } - - DatabaseException(const int errorCode) : - errorMessage_("database error " + std::to_string(errorCode) + " ocurred") - { - } - - DatabaseException(const std::string& errorMessage) : - errorMessage_(errorMessage) - { - } - - virtual const char* what() const noexcept - { - return errorMessage_.c_str(); - } - - virtual void setErrorMessage(const std::string errorMessage) - { - errorMessage_ = errorMessage; - } - }; - - class DatabaseSizeException : public obelisk::DatabaseException - { - public: - DatabaseSizeException() - { - setErrorMessage("size of string or blob exceeds limits"); - } - }; - - class DatabaseRangeException : public obelisk::DatabaseException - { - public: - DatabaseRangeException() - { - setErrorMessage("parameter index is out of range"); - } - }; - - class DatabaseMemoryException : public obelisk::DatabaseException - { - public: - DatabaseMemoryException() - { - setErrorMessage("not enough memory for operation"); - } - }; - - class DatabaseBusyException : public obelisk::DatabaseException - { - public: - DatabaseBusyException() - { - setErrorMessage("database was busy and operation was not performed"); - } - }; - - class DatabaseMisuseException : public obelisk::DatabaseException - { - public: - DatabaseMisuseException() - - { - setErrorMessage("misuse of the database routine"); - } - }; - - class DatabaseConstraintException : public obelisk::DatabaseException - { - public: - DatabaseConstraintException() - { - setErrorMessage("a constraint exception occurred"); - } - - DatabaseConstraintException(const std::string& errorMessage) - { - setErrorMessage(errorMessage); - } - }; -} // namespace obelisk - -#endif diff --git a/src/models/fact.h b/src/models/fact.h deleted file mode 100644 index 22486d3..0000000 --- a/src/models/fact.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef OBELISK_MODELS_FACT_H -#define OBELISK_MODELS_FACT_H - -#include "models/entity.h" -#include "models/fact.h" -#include "models/verb.h" - -#include - -namespace obelisk -{ - class Fact - { - private: - int id_; - obelisk::Entity leftEntity_; - obelisk::Entity rightEntity_; - obelisk::Verb verb_; - - public: - Fact() : - id_(0), - leftEntity_(), - rightEntity_(), - verb_() - { - } - - Fact(int id) : - id_(id), - leftEntity_(), - rightEntity_(), - verb_() - { - } - - Fact(obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb) : - id_(0), - leftEntity_(leftEntity), - rightEntity_(rightEntity), - verb_(verb) - { - } - - Fact(int id, obelisk::Entity leftEntity, obelisk::Entity rightEntity, obelisk::Verb verb) : - id_(id), - leftEntity_(leftEntity), - rightEntity_(rightEntity), - verb_(verb) - { - } - - static const char* createTable(); - - int& getId(); - void setId(int id); - - Entity& getLeftEntity(); - void setLeftEntity(obelisk::Entity leftEntity); - - Entity& getRightEntity(); - void setRightEntity(obelisk::Entity leftEntity); - - Verb& getVerb(); - void setVerb(obelisk::Verb verb); - - void selectFact(sqlite3* dbConnection); - void insertFact(sqlite3* dbConnection); - }; -} // namespace obelisk - -#endif diff --git a/src/models/rule.cpp b/src/models/rule.cpp deleted file mode 100644 index 7dfc4f5..0000000 --- a/src/models/rule.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "models/rule.h" - -const char* obelisk::Rule::createTable() -{ - return R"( - CREATE TABLE "rule" ( - "id" INTEGER NOT NULL UNIQUE, - "fact" INTEGER NOT NULL, - "reason" INTEGER NOT NULL CHECK("reason" != "fact"), - PRIMARY KEY("id" AUTOINCREMENT), - UNIQUE("fact", "reason"), - FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT, - FOREIGN KEY("reason") REFERENCES "fact"("id") ON DELETE RESTRICT - ); - )"; -} - -int& obelisk::Rule::getId() -{ - return id_; -} - -void obelisk::Rule::setId(int id) -{ - id_ = id; -} - -obelisk::Fact& obelisk::Rule::getFact() -{ - return fact_; -} - -void obelisk::Rule::setFact(obelisk::Fact fact) -{ - fact_ = fact; -} - -obelisk::Fact& obelisk::Rule::getReason() -{ - return reason_; -} - -void obelisk::Rule::setReason(obelisk::Fact reason) -{ - reason_ = reason; -} diff --git a/src/models/rule.h b/src/models/rule.h deleted file mode 100644 index ae75a85..0000000 --- a/src/models/rule.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef OBELISK_MODELS_RULE_H -#define OBELISK_MODELS_RULE_H - -#include "models/fact.h" - -#include - -namespace obelisk -{ - class Rule - { - private: - int id_; - obelisk::Fact fact_; - obelisk::Fact reason_; - - public: - Rule() : - id_(0), - fact_(), - reason_() - { - } - - Rule(int id) : - id_(id), - fact_(), - reason_() - { - } - - Rule(obelisk::Fact fact, obelisk::Fact reason) : - id_(0), - fact_(fact), - reason_(reason) - { - } - - Rule(int id, obelisk::Fact fact, obelisk::Fact reason) : - id_(id), - fact_(fact), - reason_(reason) - { - } - - static const char* createTable(); - - int& getId(); - void setId(int id); - - obelisk::Fact& getFact(); - void setFact(obelisk::Fact fact); - - obelisk::Fact& getReason(); - void setReason(obelisk::Fact reason); - }; -} // namespace obelisk - -#endif diff --git a/src/models/suggest_action.cpp b/src/models/suggest_action.cpp deleted file mode 100644 index 838a33b..0000000 --- a/src/models/suggest_action.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "models/suggest_action.h" - -const char* obelisk::SuggestAction::createTable() -{ - return R"( - CREATE TABLE "suggest_action" ( - "id" INTEGER NOT NULL UNIQUE, - "fact" INTEGER NOT NULL, - "true_action" INTEGER NOT NULL, - "false_action" INTEGER NOT NULL, - PRIMARY KEY("id" AUTOINCREMENT), - UNIQUE("fact", "true_action", "false_action"), - FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT, - FOREIGN KEY("true_action") REFERENCES "action"("id") ON DELETE RESTRICT, - FOREIGN KEY("false_action") REFERENCES "action"("id") ON DELETE RESTRICT - ); - )"; -} - -int& obelisk::SuggestAction::getId() -{ - return id_; -} - -void obelisk::SuggestAction::setId(int id) -{ - id_ = id; -} - -obelisk::Fact& obelisk::SuggestAction::getFact() -{ - return fact_; -} - -void obelisk::SuggestAction::setFact(obelisk::Fact fact) -{ - fact_ = fact; -} - -obelisk::Action& obelisk::SuggestAction::getTrueAction() -{ - return trueAction_; -} - -void obelisk::SuggestAction::setTrueAction(obelisk::Action trueAction) -{ - trueAction_ = trueAction; -} - -obelisk::Action& obelisk::SuggestAction::getFalseAction() -{ - return falseAction_; -} - -void obelisk::SuggestAction::setFalseAction(obelisk::Action falseAction) -{ - falseAction_ = falseAction; -} diff --git a/src/models/suggest_action.h b/src/models/suggest_action.h deleted file mode 100644 index 4e298f3..0000000 --- a/src/models/suggest_action.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef OBELISK_MODELS_SUGGEST_ACTION_H -#define OBELISK_MODELS_SUGGEST_ACTION_H - -#include "models/action.h" -#include "models/fact.h" - -#include - -namespace obelisk -{ - class SuggestAction - { - private: - int id_; - obelisk::Fact fact_; - obelisk::Action trueAction_; - obelisk::Action falseAction_; - - public: - SuggestAction() : - id_(0), - fact_(), - trueAction_(), - falseAction_() - { - } - - SuggestAction(int id) : - id_(id), - fact_(), - trueAction_(), - falseAction_() - { - } - - SuggestAction(obelisk::Fact fact, obelisk::Action trueAction, obelisk::Action falseAction) : - id_(0), - fact_(fact), - trueAction_(trueAction), - falseAction_(falseAction) - { - } - - SuggestAction(int id, obelisk::Fact fact, obelisk::Action trueAction, obelisk::Action falseAction) : - id_(id), - fact_(fact), - trueAction_(trueAction), - falseAction_(falseAction) - { - } - - static const char* createTable(); - - int& getId(); - void setId(int id); - - obelisk::Fact& getFact(); - void setFact(obelisk::Fact fact); - - obelisk::Action& getTrueAction(); - void setTrueAction(obelisk::Action trueAction); - - obelisk::Action& getFalseAction(); - void setFalseAction(obelisk::Action falseAction); - }; -} // namespace obelisk - -#endif diff --git a/src/models/verb.h b/src/models/verb.h deleted file mode 100644 index 058d1d1..0000000 --- a/src/models/verb.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef OBELISK_MODELS_VERB_H -#define OBELISK_MODELS_VERB_H - -#include - -#include - -namespace obelisk -{ - class Verb - { - private: - int id_; - std::string name_; - - public: - Verb() : - id_(0), - name_("") - { - } - - Verb(int id) : - id_(id), - name_("") - { - } - - Verb(std::string name) : - id_(0), - name_(name) - { - } - - Verb(int id, std::string name) : - id_(id), - name_(name) - { - } - - static const char* createTable(); - - int& getId(); - void setId(int id); - - std::string& getName(); - void setName(std::string name); - - void selectVerb(sqlite3* dbConnection); - void insertVerb(sqlite3* dbConnection); - }; -} // namespace obelisk - -#endif diff --git a/src/parser.cpp b/src/parser.cpp index c420a70..e5657db 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 @@ -45,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; @@ -85,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(); @@ -107,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(); @@ -177,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() @@ -191,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; @@ -202,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; } @@ -214,18 +220,416 @@ std::unique_ptr obelisk::Parser::parseExtern() return parsePrototype(); } -//action("martin" is "dangerous" then "avoid" or "ignore"); -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 = std::move(entityName); + } + else + { + rightEntity = std::move(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 '\"'"); + } + + if (getLexer()->getIdentifier() == "then") + { + break; + } + else + { + verb = getLexer()->getIdentifier(); + for (const auto& letter : verb) + { + if (!isalpha(letter)) + { + throw new obelisk::ParserException( + "non alphabetic symbol in verb"); + } + } + 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 = std::move(entityName); + } + else + { + falseAction = std::move(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 (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"); + } + + if (falseAction == "") + { + throw obelisk::ParserException("missing false action"); + } + + getNextToken(); + if (getCurrentToken() != ';') + { + throw obelisk::ParserException("missing ';'"); + } + + break; + } + + if (getCurrentToken() == '"') + { + throw obelisk::ParserException("unexpected '\"'"); + break; + } + + if (getLexer()->getIdentifier() == "else") + { + 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.setFalseAction(obelisk::Action(falseAction)); } -//rule("chris" and "martin" is "happy" if "chris" plays "playstation"); -std::unique_ptr obelisk::Parser::parseRule() +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 right 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))); } -// 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; @@ -233,20 +637,19 @@ 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('('); - // (" - bool getEntity {true}; std::vector leftEntities; std::vector rightEntities; std::string entityName {""}; std::string verb {""}; getNextToken(); - while (true) //left side of fact + while (true) { if (getEntity) { @@ -291,7 +694,17 @@ 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 ')'"); + } + else + { + syntax.pop(); + } + if (verb == "") { throw obelisk::ParserException("verb is empty"); @@ -299,20 +712,28 @@ 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(); + if (getCurrentToken() != ';') + { + throw obelisk::ParserException("missing ';'"); + } + break; } if (getCurrentToken() == '"') { throw obelisk::ParserException("unexpected '\"'"); - break; } if (getLexer()->getIdentifier() == "and") @@ -323,8 +744,15 @@ 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; } @@ -335,107 +763,242 @@ 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))); + facts.push_back(obelisk::Fact(obelisk::Entity(leftEntity), + obelisk::Entity(rightEntity), + obelisk::Verb(verb), + true)); } } } void obelisk::Parser::handleAction(std::unique_ptr& kb) { + obelisk::SuggestAction suggestAction; + + try + { + parseAction(suggestAction); + insertEntity(kb, suggestAction.getFact().getLeftEntity()); + insertEntity(kb, suggestAction.getFact().getRightEntity()); + insertVerb(kb, suggestAction.getFact().getVerb()); + insertFact(kb, suggestAction.getFact()); + insertAction(kb, suggestAction.getTrueAction()); + insertAction(kb, suggestAction.getFalseAction()); + insertSuggestAction(kb, suggestAction); + } + catch (obelisk::ParserException& exception) + { + throw; + } } void obelisk::Parser::handleRule(std::unique_ptr& kb) { + obelisk::Rule rule; + + try + { + parseRule(rule); + + 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() > 0) + { + rule.getFact().setIsTrue(1.0); + } + + 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) + { + throw; + } } 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(); + try + { + insertFact(kb, fact, true); + } + catch (obelisk::ParserException& exception) + { + throw; + } + kb->checkRule(fact); + } +} + +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::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, + bool updateIsTrue) +{ + 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) { - kb->getFact(fact); - if (fact.getId() == 0) + throw obelisk::ParserException( + "fact could not be inserted into the database"); + } + else + { + if (updateIsTrue) { - throw obelisk::ParserException("fact could not be inserted into the database"); + fact.setIsTrue(1.0); + kb->updateIsTrue(fact); } } } } -// fact("chris cromer" and "martin" and "Isabella" can "program" and "speak english"); +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"); + } + } +} + +void obelisk::Parser::insertRule(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 19b4a8b..34b4abf 100644 --- a/src/parser.h +++ b/src/parser.h @@ -6,73 +6,325 @@ #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 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); + /** + * @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); - std::unique_ptr logErrorPrototype(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(); - std::unique_ptr parseParenthesisExpression(); + + /** + * @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(); - std::unique_ptr parseAction(); - std::unique_ptr parseRule(); + + /** + * @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(); 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