Merge pull request 'feature/kb' (#14) from feature/kb into develop

Reviewed-on: #14
This commit is contained in:
Chris Cromer 2023-02-23 01:09:37 -03:00
commit 1f8cc98ee2
53 changed files with 4130 additions and 876 deletions

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -11,9 +11,28 @@
namespace obelisk
{
/**
* @brief The LLVM context.
*
*/
static std::unique_ptr<llvm::LLVMContext> TheContext;
/**
* @brief The LLVM module.
*
*/
static std::unique_ptr<llvm::Module> TheModule;
/**
* @brief The LLVM IR builder.
*
*/
static std::unique_ptr<llvm::IRBuilder<>> Builder;
/**
* @brief The LLVM named values.
*
*/
static std::map<std::string, llvm::Value *> NamedValues;
} // namespace obelisk

View File

@ -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<std::unique_ptr<ExpressionAST>> 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<std::unique_ptr<ExpressionAST>> Returns an
* AST expression containing the args.
*/
std::vector<std::unique_ptr<ExpressionAST>> getArgs();
/**
* @brief Set the arguments to be used by the function.
*
* @param[in] args The args to set.
*/
void setArgs(std::vector<std::unique_ptr<ExpressionAST>> args);
public:
CallExpressionAST(const std::string &callee, std::vector<std::unique_ptr<ExpressionAST>> 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<std::unique_ptr<ExpressionAST>> args) :
callee_(callee),
args_(std::move(args))
{
}
/**
* @brief Generate the calle IR code.
*
* @return llvm::Value*
*/
llvm::Value *codegen() override;
};
} // namespace obelisk

View File

@ -7,8 +7,22 @@
namespace obelisk
{
std::unique_ptr<ExpressionAST> 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<ExpressionAST> Returns the AST expression that
* caused the error.
*/
std::unique_ptr<ExpressionAST> 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

View File

@ -5,10 +5,25 @@
namespace obelisk
{
/**
* @brief A generic AST expression which other expression will inherit from.
*
*/
class ExpressionAST
{
public:
/**
* @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

View File

@ -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();

View File

@ -8,22 +8,58 @@
namespace obelisk
{
/**
* @brief A Funcion AST node.
*
*/
class FunctionAST
{
private:
/**
* @brief The prototype of the function.
*
*/
std::unique_ptr<PrototypeAST> prototype_;
/**
* @brief The body of the function.
*
*/
std::unique_ptr<ExpressionAST> body_;
/**
* @brief Get the prototype.
*
* @return std::unique_ptr<PrototypeAST> Returns the prototype AST.
*/
std::unique_ptr<PrototypeAST> getPrototype();
/**
* @brief Set the prototype.
*
* @param[in] prototype Set the prototype.
*/
void setPrototype(std::unique_ptr<PrototypeAST> prototype);
public:
FunctionAST(std::unique_ptr<PrototypeAST> prototype, std::unique_ptr<ExpressionAST> 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<PrototypeAST> prototype,
std::unique_ptr<ExpressionAST> 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

View File

@ -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

View File

@ -3,10 +3,17 @@
llvm::Function *obelisk::PrototypeAST::codegen()
{
std::vector<llvm::Type *> doubles(args_.size(), llvm::Type::getDoubleTy(*TheContext));
llvm::FunctionType *FT = llvm::FunctionType::get(llvm::Type::getDoubleTy(*TheContext), doubles, false);
std::vector<llvm::Type *> 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())

View File

@ -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<std::string> 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<std::string> Returns the arguments.
*/
std::vector<std::string> getArgs();
/**
* @brief Set the arguments the prototype accepts.
*
* @param[in] args The arguments.
*/
void setArgs(std::vector<std::string> args);
public:
PrototypeAST(const std::string& name, std::vector<std::string> 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<std::string> 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

View File

@ -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

View File

@ -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 <cstring>
#include <filesystem>
#include <iostream>
#include <string>
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<const char*()> 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<obelisk::Entity>& 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<obelisk::Verb>& 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<obelisk::Fact>& 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);
}

View File

@ -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")

82
src/lib/include/obelisk.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef OBELISK_INCLUDE_OBELISK_H
#define OBELISK_INCLUDE_OBELISK_H
#include "knowledge_base.h"
#include <memory>
#include <string>
/**
* @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<obelisk::KnowledgeBase> 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

View File

@ -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

284
src/lib/knowledge_base.cpp Normal file
View File

@ -0,0 +1,284 @@
#include "knowledge_base.h"
#include "models/error.h"
#include <cstring>
#include <filesystem>
#include <iostream>
#include <string>
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<const char*()> 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<obelisk::Entity>& 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<obelisk::Verb>& 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<obelisk::Action>& 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<obelisk::Fact>& 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<obelisk::SuggestAction>& 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<obelisk::Rule>& 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<obelisk::Rule> 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);
}

View File

@ -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 <sqlite3.h>
@ -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<obelisk::Entity>& 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<obelisk::Verb>& 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<obelisk::Action>& 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<obelisk::Fact>& 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<obelisk::SuggestAction>& 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<obelisk::Rule>& 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.

36
src/lib/meson.build Normal file
View File

@ -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
)

168
src/lib/models/action.cpp Normal file
View File

@ -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;
}

View File

@ -1,6 +1,8 @@
#ifndef OBELISK_MODELS_ACTION_H
#define OBELISK_MODELS_ACTION_H
#include <sqlite3.h>
#include <string>
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

View File

@ -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;

View File

@ -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

193
src/lib/models/error.h Normal file
View File

@ -0,0 +1,193 @@
#ifndef OBELISK_MODELS_ERROR_H
#define OBELISK_MODELS_ERROR_H
#include <exception>
#include <string>
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

600
src/lib/models/fact.cpp Normal file
View File

@ -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;
}

247
src/lib/models/fact.h Normal file
View File

@ -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 <string>
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

286
src/lib/models/rule.cpp Normal file
View File

@ -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<obelisk::Rule>& 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;
}

163
src/lib/models/rule.h Normal file
View File

@ -0,0 +1,163 @@
#ifndef OBELISK_MODELS_RULE_H
#define OBELISK_MODELS_RULE_H
#include "models/fact.h"
#include <string>
#include <vector>
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<obelisk::Rule>& rules);
/**
* @brief Insert the Rule into the KnowledgeBase.
*
* @param[in] dbConnection The database connection to use.
*/
void insert(sqlite3* dbConnection);
};
} // namespace obelisk
#endif

View File

@ -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;
}

View File

@ -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 <string>
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

View File

@ -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;

126
src/lib/models/verb.h Normal file
View File

@ -0,0 +1,126 @@
#ifndef OBELISK_MODELS_VERB_H
#define OBELISK_MODELS_VERB_H
#include <sqlite3.h>
#include <string>
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

41
src/lib/obelisk.c Normal file
View File

@ -0,0 +1,41 @@
#include "obelisk_c.h"
#include "obelisk_wrapper.h"
#include <stdlib.h>
#include <string.h>
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);
}

47
src/lib/obelisk.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "include/obelisk.h"
#include "version.h"
obelisk::Obelisk::Obelisk(std::string filename)
{
kb_ = std::unique_ptr<obelisk::KnowledgeBase> {
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();
}

View File

@ -0,0 +1,61 @@
#include "obelisk.h"
#include "obelisk_wrapper.h"
#include <string.h>
extern "C"
{
CObelisk* create_obelisk(const char* filename)
{
obelisk::Obelisk* obelisk = new obelisk::Obelisk(filename);
return reinterpret_cast<CObelisk*>(obelisk);
}
char* call_obelisk_getVersion(CObelisk* p_obelisk)
{
obelisk::Obelisk* obelisk
= reinterpret_cast<obelisk::Obelisk*>(p_obelisk);
auto version = strdup(obelisk->getVersion().c_str());
return version;
}
int call_obelisk_getLibVersion(CObelisk* p_obelisk)
{
obelisk::Obelisk* obelisk
= reinterpret_cast<obelisk::Obelisk*>(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<obelisk::Obelisk*>(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<obelisk::Obelisk*>(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<obelisk::Obelisk*>(p_obelisk);
delete obelisk;
}
};

76
src/lib/obelisk_wrapper.h Normal file
View File

@ -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

21
src/lib/version.h.in Normal file
View File

@ -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

View File

@ -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 <limits>
#include <memory>
int obelisk::mainLoop(const std::vector<std::string>& sourceFiles, const std::string& kbFile)
int obelisk::mainLoop(const std::vector<std::string>& sourceFiles,
const std::string& kbFile)
{
std::unique_ptr<obelisk::KnowledgeBase> kb;
try
{
kb = std::unique_ptr<obelisk::KnowledgeBase> {new obelisk::KnowledgeBase(kbFile.c_str())};
kb = std::unique_ptr<obelisk::KnowledgeBase> {
new obelisk::KnowledgeBase(kbFile.c_str())};
}
catch (obelisk::KnowledgeBaseException& exception)
{
@ -27,7 +30,8 @@ int obelisk::mainLoop(const std::vector<std::string>& sourceFiles, const std::st
std::shared_ptr<obelisk::Lexer> lexer;
try
{
lexer = std::shared_ptr<obelisk::Lexer> {new obelisk::Lexer(sourceFiles[file++])};
lexer = std::shared_ptr<obelisk::Lexer> {
new obelisk::Lexer(sourceFiles[file++])};
}
catch (obelisk::LexerException& exception)
{
@ -52,14 +56,16 @@ int obelisk::mainLoop(const std::vector<std::string>& 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<obelisk::Lexer> {new obelisk::Lexer(sourceFiles[file++])};
lexer = std::shared_ptr<obelisk::Lexer> {
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<std::string>& sourceFiles, const std::st
}
break;
case obelisk::Lexer::kTokenFact :
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);

View File

@ -1,7 +1,10 @@
#ifndef OBELISK_MAIN_H
#define OBELISK_MAIN_H
#include <getopt.h>
/**
* @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<std::string> &sourceFiles, const std::string &kbFile);
int mainLoop(const std::vector<std::string> &sourceFiles,
const std::string &kbFile);
} // namespace obelisk
#endif

View File

@ -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

View File

@ -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;
}

View File

@ -1,102 +0,0 @@
#ifndef OBELISK_MODELS_ERROR_H
#define OBELISK_MODELS_ERROR_H
#include <exception>
#include <string>
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

View File

@ -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 <string>
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

View File

@ -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;
}

View File

@ -1,59 +0,0 @@
#ifndef OBELISK_MODELS_RULE_H
#define OBELISK_MODELS_RULE_H
#include "models/fact.h"
#include <string>
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

View File

@ -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;
}

View File

@ -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 <string>
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

View File

@ -1,54 +0,0 @@
#ifndef OBELISK_MODELS_VERB_H
#define OBELISK_MODELS_VERB_H
#include <sqlite3.h>
#include <string>
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

View File

@ -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 <memory>
@ -45,13 +42,15 @@ void obelisk::Parser::setCurrentToken(int currentToken)
currentToken_ = currentToken;
}
std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::logError(const char* str)
std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::logError(
const char* str)
{
fprintf(stderr, "Error: %s\n", str);
return nullptr;
}
std::unique_ptr<obelisk::PrototypeAST> obelisk::Parser::logErrorPrototype(const char* str)
std::unique_ptr<obelisk::PrototypeAST> obelisk::Parser::logErrorPrototype(
const char* str)
{
logError(str);
return nullptr;
@ -85,12 +84,14 @@ std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parsePrimary()
std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseNumberExpression()
{
auto result = std::make_unique<obelisk::NumberExpressionAST>(getLexer()->getNumberValue());
auto result = std::make_unique<obelisk::NumberExpressionAST>(
getLexer()->getNumberValue());
getNextToken();
return result;
}
std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseParenthesisExpression()
std::unique_ptr<obelisk::ExpressionAST>
obelisk::Parser::parseParenthesisExpression()
{
getNextToken();
auto v = parseExpression();
@ -107,7 +108,8 @@ std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseParenthesisExpress
return v;
}
std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseIdentifierExpression()
std::unique_ptr<obelisk::ExpressionAST>
obelisk::Parser::parseIdentifierExpression()
{
std::string idName = getLexer()->getIdentifier();
getNextToken();
@ -177,7 +179,8 @@ std::unique_ptr<obelisk::PrototypeAST> obelisk::Parser::parsePrototype()
getNextToken();
return std::make_unique<obelisk::PrototypeAST>(functionName, std::move(argNames));
return std::make_unique<obelisk::PrototypeAST>(functionName,
std::move(argNames));
}
std::unique_ptr<obelisk::FunctionAST> obelisk::Parser::parseDefinition()
@ -191,7 +194,8 @@ std::unique_ptr<obelisk::FunctionAST> obelisk::Parser::parseDefinition()
if (auto expression = parseExpression())
{
return std::make_unique<FunctionAST>(std::move(prototype), std::move(expression));
return std::make_unique<FunctionAST>(std::move(prototype),
std::move(expression));
}
return nullptr;
@ -202,8 +206,10 @@ std::unique_ptr<obelisk::FunctionAST> obelisk::Parser::parseTopLevelExpression()
if (auto expression = parseExpression())
{
// Make an anonymous prototype
auto prototype = std::make_unique<obelisk::PrototypeAST>("__anon_expr", std::vector<std::string>());
return std::make_unique<obelisk::FunctionAST>(std::move(prototype), std::move(expression));
auto prototype = std::make_unique<obelisk::PrototypeAST>("__anon_expr",
std::vector<std::string>());
return std::make_unique<obelisk::FunctionAST>(std::move(prototype),
std::move(expression));
}
return nullptr;
}
@ -214,18 +220,416 @@ std::unique_ptr<obelisk::PrototypeAST> obelisk::Parser::parseExtern()
return parsePrototype();
}
//action("martin" is "dangerous" then "avoid" or "ignore");
std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseAction()
void obelisk::Parser::parseAction(obelisk::SuggestAction& suggestAction)
{
std::stack<char> 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::ExpressionAST> obelisk::Parser::parseRule()
void obelisk::Parser::parseRule(obelisk::Rule& rule)
{
std::stack<char> 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<obelisk::Fact>& facts)
{
std::stack<char> syntax;
@ -233,20 +637,19 @@ void obelisk::Parser::parseFact(std::vector<obelisk::Fact>& 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<std::string> leftEntities;
std::vector<std::string> 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<obelisk::Fact>& 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<obelisk::Fact>& 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")
@ -324,7 +745,14 @@ void obelisk::Parser::parseFact(std::vector<obelisk::Fact>& facts)
else
{
verb = getLexer()->getIdentifier();
// TODO: make sure verb is alphabetic
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<obelisk::Fact>& 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& kb)
{
std::vector<obelisk::Fact> facts;
try
{
parseFact(facts);
}
catch (obelisk::ParserException& exception)
{
throw;
}
int verbId = 0;
for (auto& fact : facts)
{
std::vector<obelisk::Entity> 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");
insertEntity(kb, fact.getLeftEntity());
insertEntity(kb, fact.getRightEntity());
}
else
catch (obelisk::ParserException& exception)
{
fact.setLeftEntity(entity);
}
}
entities = {fact.getRightEntity()};
kb->addEntities(entities);
fact.setRightEntity(entities.front());
if (fact.getRightEntity().getId() == 0)
{
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<obelisk::Verb> verbs = {fact.getVerb()};
kb->addVerbs(verbs);
if (verbs.front().getId() != 0)
try
{
fact.setVerb(verbs.front());
insertVerb(kb, fact.getVerb());
}
catch (obelisk::ParserException& exception)
{
throw;
}
verbId = fact.getVerb().getId();
}
else
{
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();
}
}
}
else
{
fact.getVerb().setId(verbId);
}
try
{
insertFact(kb, fact, true);
}
catch (obelisk::ParserException& exception)
{
throw;
}
kb->checkRule(fact);
}
}
void obelisk::Parser::insertEntity(std::unique_ptr<obelisk::KnowledgeBase>& kb,
obelisk::Entity& entity)
{
std::vector<obelisk::Entity> 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<obelisk::KnowledgeBase>& kb,
obelisk::Verb& verb)
{
std::vector<obelisk::Verb> 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<obelisk::KnowledgeBase>& kb,
obelisk::Action& action)
{
std::vector<obelisk::Action> 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<obelisk::KnowledgeBase>& kb,
obelisk::Fact& fact,
bool updateIsTrue)
{
std::vector<obelisk::Fact> facts {fact};
kb->addFacts(facts);
fact = facts.front();
fact = std::move(facts.front());
// the id was not inserted, so check if it exists in the database
if (fact.getId() == 0)
{
kb->getFact(fact);
if (fact.getId() == 0)
{
throw obelisk::ParserException("fact could not be inserted into the database");
throw obelisk::ParserException(
"fact could not be inserted into the database");
}
else
{
if (updateIsTrue)
{
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<obelisk::KnowledgeBase>& kb,
obelisk::SuggestAction& suggestAction)
{
std::vector<obelisk::SuggestAction> 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<obelisk::KnowledgeBase>& kb,
obelisk::Rule& rule)
{
std::vector<obelisk::Rule> 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");
}
}
}

View File

@ -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 <memory>
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<obelisk::Lexer> 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<obelisk::ExpressionAST> Returns the AST
* expression that caused the error.
*/
std::unique_ptr<obelisk::ExpressionAST> logError(const char* str);
std::unique_ptr<obelisk::PrototypeAST> logErrorPrototype(const char* str);
/**
* @brief Log errors from the LLVM parsing involving the prototypes.
*
* @param[in] str The error message.
* @return std::unique_ptr<obelisk::PrototypeAST> Returns the AST
* for the prototype.
*/
std::unique_ptr<obelisk::PrototypeAST> logErrorPrototype(
const char* str);
/**
* @brief The AST expression parser.
*
* @return std::unique_ptr<obelisk::ExpressionAST> Returns the
* parsed AST expression.
*/
std::unique_ptr<obelisk::ExpressionAST> parseExpression();
/**
* @brief The AST number expression parser.
*
* @return std::unique_ptr<obelisk::ExpressionAST> Returns the
* parsed AST expression.
*/
std::unique_ptr<obelisk::ExpressionAST> parseNumberExpression();
std::unique_ptr<obelisk::ExpressionAST> parseParenthesisExpression();
/**
* @brief The AST parenthesis expression parser.
*
* @return std::unique_ptr<obelisk::ExpressionAST> Returns the
* parsed AST expression.
*/
std::unique_ptr<obelisk::ExpressionAST>
parseParenthesisExpression();
/**
* @brief The AST identifier expression parser.
*
* @return std::unique_ptr<obelisk::ExpressionAST> Returns the
* parsed AST expression.
*/
std::unique_ptr<obelisk::ExpressionAST> parseIdentifierExpression();
/**
* @brief The AST primary expression parser.
*
* @return std::unique_ptr<obelisk::ExpressionAST> Returns the
* parsed AST expression.
*/
std::unique_ptr<obelisk::ExpressionAST> parsePrimary();
/**
* @brief The AST prototype parser.
*
* @return std::unique_ptr<obelisk::PrototypeAST> Returns the parsed
* AST prototype expression.
*/
std::unique_ptr<obelisk::PrototypeAST> parsePrototype();
/**
* @brief The AST definition parser.
*
* @return std::unique_ptr<obelisk::FunctionAST> Returns the parsed
* AST definition expression.
*/
std::unique_ptr<obelisk::FunctionAST> parseDefinition();
/**
* @brief The AST top level expression parser.
*
* @return std::unique_ptr<obelisk::FunctionAST> Returns the parsed
* AST top level expression.
*/
std::unique_ptr<obelisk::FunctionAST> parseTopLevelExpression();
/**
* @brief The AST external definition parser.
*
* @return std::unique_ptr<obelisk::PrototypeAST> Returns the parsed
* AST external definition.
*/
std::unique_ptr<obelisk::PrototypeAST> parseExtern();
std::unique_ptr<obelisk::ExpressionAST> parseAction();
std::unique_ptr<obelisk::ExpressionAST> 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<obelisk::Fact>& 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<obelisk::Lexer> lexer) :
lexer_(lexer)
{
}
/**
* @brief Get the Lexer.
*
* @return std::shared_ptr<obelisk::Lexer> Returns the current Lexer
* in use by the Parser.
*/
std::shared_ptr<obelisk::Lexer> getLexer();
/**
* @brief Set the Lexer to use during the parsing phase.
*
* @param[in] lexer The Lexer.
*/
void setLexer(std::shared_ptr<obelisk::Lexer> 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& 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<obelisk::KnowledgeBase>& 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();

View File

@ -1,8 +0,0 @@
namespace obelisk
{
/**
* @brief The current version of obelisk.
*
*/
std::string version = "@version@";
} // namespace obelisk