feature/fact_knowledge_base #7

Merged
cromer merged 7 commits from feature/fact_knowledge_base into develop 2022-11-29 00:07:51 -03:00
19 changed files with 975 additions and 139 deletions

View File

@ -1,18 +1,16 @@
#include "knowledge_base.h" #include "knowledge_base.h"
#include "models/action.h" #include "models/action.h"
#include "models/entity.h" #include "models/entity.h"
#include "models/error.h"
#include "models/fact.h" #include "models/fact.h"
#include "models/rule.h" #include "models/rule.h"
#include "models/suggest_action.h" #include "models/suggest_action.h"
#include "models/verb.h" #include "models/verb.h"
#include <cstring>
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
#include <string>
obelisk::KnowledgeBase::KnowledgeBase(const char* filename)
{
KnowledgeBase(filename, DEFAULT_FLAGS);
}
obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags) obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags)
{ {
@ -28,6 +26,8 @@ obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags)
logSqliteError(result); logSqliteError(result);
} }
enableForeignKeys();
if (!dbExists) if (!dbExists)
{ {
createTable(obelisk::Action::createTable); createTable(obelisk::Action::createTable);
@ -47,16 +47,25 @@ obelisk::KnowledgeBase::~KnowledgeBase()
} }
} }
void obelisk::KnowledgeBase::createTable(std::function<const char*()> function) /**
* @brief Enable foreign key functionality in the open database.
*
* This must always be done when the connection is opened or it will not
* enforce the foreign key constraints.
*/
void obelisk::KnowledgeBase::enableForeignKeys()
{ {
char* tmp; char* errmsg;
auto result = sqlite3_exec(dbConnection_, function(), NULL, NULL, &tmp); int result = sqlite3_exec(dbConnection_,
"PRAGMA foreign_keys = ON;",
NULL,
NULL,
&errmsg);
if (result != SQLITE_OK) if (result != SQLITE_OK)
{ {
logSqliteError(result); logSqliteError(result);
if (tmp) if (errmsg)
{ {
std::string errmsg(tmp);
throw obelisk::KnowledgeBaseException(errmsg); throw obelisk::KnowledgeBaseException(errmsg);
} }
else else
@ -66,7 +75,103 @@ void obelisk::KnowledgeBase::createTable(std::function<const char*()> function)
} }
} }
// TODO: log files? void obelisk::KnowledgeBase::createTable(std::function<const char*()> function)
{
char* errmsg;
int result = sqlite3_exec(dbConnection_, function(), NULL, NULL, &errmsg);
if (result != SQLITE_OK)
{
logSqliteError(result);
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::DatabaseException::ConstraintException& 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::DatabaseException::ConstraintException& 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::DatabaseException::ConstraintException& 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_);
}
// TODO: log files? or just throw an error?
void obelisk::KnowledgeBase::logSqliteError(int result) void obelisk::KnowledgeBase::logSqliteError(int result)
{ {
std::cout << sqlite3_errstr(result) << std::endl; std::cout << sqlite3_errstr(result) << std::endl;

View File

@ -1,10 +1,15 @@
#ifndef OBELISK_KNOWLEDGE_BASE_H #ifndef OBELISK_KNOWLEDGE_BASE_H
#define OBELISK_KNOWLEDGE_BASE_H #define OBELISK_KNOWLEDGE_BASE_H
#include "models/entity.h"
#include "models/fact.h"
#include "models/verb.h"
#include <sqlite3.h> #include <sqlite3.h>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <memory>
#include <string> #include <string>
namespace obelisk namespace obelisk
@ -19,19 +24,27 @@ namespace obelisk
int flags_; int flags_;
void logSqliteError(int result); void logSqliteError(int result);
void enableForeignKeys();
void createTable(std::function<const char*()> function); void createTable(std::function<const char*()> function);
public: public:
KnowledgeBase(const char* filename);
KnowledgeBase(const char* filename, int flags); KnowledgeBase(const char* filename, int flags);
KnowledgeBase(const char* filename) :
KnowledgeBase(filename,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
{
}
~KnowledgeBase(); ~KnowledgeBase();
template<typename T, typename U> void addEntities(std::vector<obelisk::Entity>& entities);
int addFacts(std::string verb, T leftEntities, U rightEntities); void addVerbs(std::vector<obelisk::Verb>& verbs);
// TODO: add parameter for fact void addFacts(std::vector<obelisk::Fact>& facts);
template<typename T, typename U>
int addRules(std::string verb, T leftEntities, U rightEntities); void getEntity(obelisk::Entity& entity);
template<typename T, typename U> int addActions(); void getVerb(obelisk::Verb& verb);
void getFact(obelisk::Fact& fact);
void getDouble(double& result, float var1, float var2); void getDouble(double& result, float var1, float var2);
void getFloat(float& result1, float& result2, double var); void getFloat(float& result1, float& result2, double var);

View File

@ -11,7 +11,7 @@ const char* obelisk::Action::createTable()
)"; )";
} }
int obelisk::Action::getId() int& obelisk::Action::getId()
{ {
return id_; return id_;
} }
@ -21,7 +21,7 @@ void obelisk::Action::setId(int id)
id_ = id; id_ = id;
} }
std::string obelisk::Action::getName() std::string& obelisk::Action::getName()
{ {
return name_; return name_;
} }

View File

@ -38,10 +38,10 @@ namespace obelisk
static const char* createTable(); static const char* createTable();
int getId(); int& getId();
void setId(int id); void setId(int id);
std::string getName(); std::string& getName();
void setName(std::string name); void setName(std::string name);
}; };
} // namespace obelisk } // namespace obelisk

View File

@ -1,4 +1,5 @@
#include "models/entity.h" #include "models/entity.h"
#include "models/error.h"
const char* obelisk::Entity::createTable() const char* obelisk::Entity::createTable()
{ {
@ -11,7 +12,142 @@ const char* obelisk::Entity::createTable()
)"; )";
} }
int obelisk::Entity::getId() void obelisk::Entity::selectEntity(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 entity 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::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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::DatabaseException::BusyException();
break;
case SQLITE_MISUSE :
throw obelisk::DatabaseException::MisuseException();
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::Entity::insertEntity(sqlite3* dbConnection)
{
if (dbConnection == nullptr)
{
throw obelisk::DatabaseException("database isn't open");
}
sqlite3_stmt* 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);
switch (result)
{
case SQLITE_OK :
break;
case SQLITE_TOOBIG :
throw obelisk::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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::DatabaseException::ConstraintException(
sqlite3_errmsg(dbConnection));
case SQLITE_BUSY :
throw obelisk::DatabaseException::BusyException();
break;
case SQLITE_MISUSE :
throw obelisk::DatabaseException::MisuseException();
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::Entity::getId()
{ {
return id_; return id_;
} }
@ -21,7 +157,7 @@ void obelisk::Entity::setId(int id)
id_ = id; id_ = id;
} }
std::string obelisk::Entity::getName() std::string& obelisk::Entity::getName()
{ {
return name_; return name_;
} }

View File

@ -1,6 +1,8 @@
#ifndef OBELISK_MODELS_ENTITY_H #ifndef OBELISK_MODELS_ENTITY_H
#define OBELISK_MODELS_ENTITY_H #define OBELISK_MODELS_ENTITY_H
#include <sqlite3.h>
#include <string> #include <string>
namespace obelisk namespace obelisk
@ -38,11 +40,14 @@ namespace obelisk
static const char* createTable(); static const char* createTable();
int getId(); int& getId();
void setId(int id); void setId(int id);
std::string getName(); std::string& getName();
void setName(std::string name); void setName(std::string name);
void selectEntity(sqlite3* dbConnection);
void insertEntity(sqlite3* dbConnection);
}; };
} // namespace obelisk } // namespace obelisk

146
src/models/error.h Normal file
View File

@ -0,0 +1,146 @@
#ifndef OBELISK_MODELS_ERROR_H
#define OBELISK_MODELS_ERROR_H
#include <exception>
#include <string>
namespace obelisk
{
class DatabaseException : public std::exception
{
private:
const 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)
{
}
const char* what() const noexcept
{
return errorMessage_.c_str();
}
class SizeException : public std::exception
{
private:
const std::string errorMessage_;
public:
SizeException() :
errorMessage_("size of string or blob exceeds limits")
{
}
const char* what() const noexcept
{
return errorMessage_.c_str();
}
};
class RangeException : public std::exception
{
private:
const std::string errorMessage_;
public:
RangeException() :
errorMessage_("parameter index is out of range")
{
}
const char* what() const noexcept
{
return errorMessage_.c_str();
}
};
class MemoryException : public std::exception
{
private:
const std::string errorMessage_;
public:
MemoryException() :
errorMessage_("not enough memory for operation")
{
}
const char* what() const noexcept
{
return errorMessage_.c_str();
}
};
class BusyException : public std::exception
{
private:
const std::string errorMessage_;
public:
BusyException() :
errorMessage_(
"database was busy and operation not performed")
{
}
const char* what() const noexcept
{
return errorMessage_.c_str();
}
};
class MisuseException : public std::exception
{
private:
const std::string errorMessage_;
public:
MisuseException() :
errorMessage_("misuse of the database routine")
{
}
const char* what() const noexcept
{
return errorMessage_.c_str();
}
};
class ConstraintException : public std::exception
{
private:
const std::string errorMessage_;
public:
ConstraintException() :
errorMessage_("a constraint exception occurred")
{
}
ConstraintException(const std::string& errorMessage) :
errorMessage_(errorMessage)
{
}
const char* what() const noexcept
{
return errorMessage_.c_str();
}
};
};
} // namespace obelisk
#endif

View File

@ -1,3 +1,4 @@
#include "models/error.h"
#include "models/fact.h" #include "models/fact.h"
const char* obelisk::Fact::createTable() const char* obelisk::Fact::createTable()
@ -9,6 +10,7 @@ const char* obelisk::Fact::createTable()
"right_entity" INTEGER NOT NULL, "right_entity" INTEGER NOT NULL,
"verb" INTEGER NOT NULL, "verb" INTEGER NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT), PRIMARY KEY("id" AUTOINCREMENT),
UNIQUE("left_entity", "right_entity", "verb")
FOREIGN KEY("verb") REFERENCES "verb"("id") ON DELETE RESTRICT, FOREIGN KEY("verb") REFERENCES "verb"("id") ON DELETE RESTRICT,
FOREIGN KEY("right_entity") REFERENCES "entity"("id") ON DELETE RESTRICT, FOREIGN KEY("right_entity") REFERENCES "entity"("id") ON DELETE RESTRICT,
FOREIGN KEY("left_entity") REFERENCES "entity"("id") ON DELETE RESTRICT FOREIGN KEY("left_entity") REFERENCES "entity"("id") ON DELETE RESTRICT
@ -16,7 +18,217 @@ const char* obelisk::Fact::createTable()
)"; )";
} }
int obelisk::Fact::getId() void obelisk::Fact::selectFact(sqlite3* dbConnection)
{
if (dbConnection == nullptr)
{
throw obelisk::DatabaseException("database isn't open");
}
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=?)",
-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::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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));
break;
case SQLITE_BUSY :
throw obelisk::DatabaseException::BusyException();
break;
case SQLITE_MISUSE :
throw obelisk::DatabaseException::MisuseException();
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::insertFact(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) 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::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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::DatabaseException::ConstraintException(
sqlite3_errmsg(dbConnection));
case SQLITE_BUSY :
throw obelisk::DatabaseException::BusyException();
break;
case SQLITE_MISUSE :
throw obelisk::DatabaseException::MisuseException();
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_; return id_;
} }
@ -26,7 +238,7 @@ void obelisk::Fact::setId(int id)
id_ = id; id_ = id;
} }
obelisk::Entity obelisk::Fact::getLeftEntity() obelisk::Entity& obelisk::Fact::getLeftEntity()
{ {
return leftEntity_; return leftEntity_;
} }
@ -36,7 +248,7 @@ void obelisk::Fact::setLeftEntity(obelisk::Entity leftEntity)
leftEntity_ = leftEntity; leftEntity_ = leftEntity;
} }
obelisk::Entity obelisk::Fact::getRightEntity() obelisk::Entity& obelisk::Fact::getRightEntity()
{ {
return rightEntity_; return rightEntity_;
} }
@ -46,7 +258,7 @@ void obelisk::Fact::setRightEntity(obelisk::Entity rightEntity)
rightEntity_ = rightEntity; rightEntity_ = rightEntity;
} }
obelisk::Verb obelisk::Fact::getVerb() obelisk::Verb& obelisk::Fact::getVerb()
{ {
return verb_; return verb_;
} }

View File

@ -2,6 +2,7 @@
#define OBELISK_MODELS_FACT_H #define OBELISK_MODELS_FACT_H
#include "models/entity.h" #include "models/entity.h"
#include "models/fact.h"
#include "models/verb.h" #include "models/verb.h"
#include <string> #include <string>
@ -56,17 +57,20 @@ namespace obelisk
static const char* createTable(); static const char* createTable();
int getId(); int& getId();
void setId(int id); void setId(int id);
obelisk::Entity getLeftEntity(); Entity& getLeftEntity();
void setLeftEntity(obelisk::Entity leftEntity); void setLeftEntity(obelisk::Entity leftEntity);
obelisk::Entity getRightEntity(); Entity& getRightEntity();
void setRightEntity(obelisk::Entity leftEntity); void setRightEntity(obelisk::Entity leftEntity);
obelisk::Verb getVerb(); Verb& getVerb();
void setVerb(obelisk::Verb verb); void setVerb(obelisk::Verb verb);
void selectFact(sqlite3* dbConnection);
void insertFact(sqlite3* dbConnection);
}; };
} // namespace obelisk } // namespace obelisk

View File

@ -8,13 +8,14 @@ const char* obelisk::Rule::createTable()
"fact" INTEGER NOT NULL, "fact" INTEGER NOT NULL,
"reason" INTEGER NOT NULL CHECK("reason" != "fact"), "reason" INTEGER NOT NULL CHECK("reason" != "fact"),
PRIMARY KEY("id" AUTOINCREMENT), PRIMARY KEY("id" AUTOINCREMENT),
UNIQUE("fact", "reason"),
FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT, FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT,
FOREIGN KEY("reason") REFERENCES "fact"("id") ON DELETE RESTRICT FOREIGN KEY("reason") REFERENCES "fact"("id") ON DELETE RESTRICT
); );
)"; )";
} }
int obelisk::Rule::getId() int& obelisk::Rule::getId()
{ {
return id_; return id_;
} }
@ -24,7 +25,7 @@ void obelisk::Rule::setId(int id)
id_ = id; id_ = id;
} }
obelisk::Fact obelisk::Rule::getFact() obelisk::Fact& obelisk::Rule::getFact()
{ {
return fact_; return fact_;
} }
@ -34,7 +35,7 @@ void obelisk::Rule::setFact(obelisk::Fact fact)
fact_ = fact; fact_ = fact;
} }
obelisk::Fact obelisk::Rule::getReason() obelisk::Fact& obelisk::Rule::getReason()
{ {
return reason_; return reason_;
} }

View File

@ -45,13 +45,13 @@ namespace obelisk
static const char* createTable(); static const char* createTable();
int getId(); int& getId();
void setId(int id); void setId(int id);
obelisk::Fact getFact(); obelisk::Fact& getFact();
void setFact(obelisk::Fact fact); void setFact(obelisk::Fact fact);
obelisk::Fact getReason(); obelisk::Fact& getReason();
void setReason(obelisk::Fact reason); void setReason(obelisk::Fact reason);
}; };
} // namespace obelisk } // namespace obelisk

View File

@ -9,6 +9,7 @@ const char* obelisk::SuggestAction::createTable()
"true_action" INTEGER NOT NULL, "true_action" INTEGER NOT NULL,
"false_action" INTEGER NOT NULL, "false_action" INTEGER NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT), PRIMARY KEY("id" AUTOINCREMENT),
UNIQUE("fact", "true_action", "false_action"),
FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT, FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT,
FOREIGN KEY("true_action") REFERENCES "action"("id") ON DELETE RESTRICT, FOREIGN KEY("true_action") REFERENCES "action"("id") ON DELETE RESTRICT,
FOREIGN KEY("false_action") REFERENCES "action"("id") ON DELETE RESTRICT FOREIGN KEY("false_action") REFERENCES "action"("id") ON DELETE RESTRICT
@ -16,7 +17,7 @@ const char* obelisk::SuggestAction::createTable()
)"; )";
} }
int obelisk::SuggestAction::getId() int& obelisk::SuggestAction::getId()
{ {
return id_; return id_;
} }
@ -26,7 +27,7 @@ void obelisk::SuggestAction::setId(int id)
id_ = id; id_ = id;
} }
obelisk::Fact obelisk::SuggestAction::getFact() obelisk::Fact& obelisk::SuggestAction::getFact()
{ {
return fact_; return fact_;
} }
@ -36,7 +37,7 @@ void obelisk::SuggestAction::setFact(obelisk::Fact fact)
fact_ = fact; fact_ = fact;
} }
obelisk::Action obelisk::SuggestAction::getTrueAction() obelisk::Action& obelisk::SuggestAction::getTrueAction()
{ {
return trueAction_; return trueAction_;
} }
@ -46,7 +47,7 @@ void obelisk::SuggestAction::setTrueAction(obelisk::Action trueAction)
trueAction_ = trueAction; trueAction_ = trueAction;
} }
obelisk::Action obelisk::SuggestAction::getFalseAction() obelisk::Action& obelisk::SuggestAction::getFalseAction()
{ {
return falseAction_; return falseAction_;
} }

View File

@ -56,16 +56,16 @@ namespace obelisk
static const char* createTable(); static const char* createTable();
int getId(); int& getId();
void setId(int id); void setId(int id);
obelisk::Fact getFact(); obelisk::Fact& getFact();
void setFact(obelisk::Fact fact); void setFact(obelisk::Fact fact);
obelisk::Action getTrueAction(); obelisk::Action& getTrueAction();
void setTrueAction(obelisk::Action trueAction); void setTrueAction(obelisk::Action trueAction);
obelisk::Action getFalseAction(); obelisk::Action& getFalseAction();
void setFalseAction(obelisk::Action falseAction); void setFalseAction(obelisk::Action falseAction);
}; };
} // namespace obelisk } // namespace obelisk

View File

@ -1,5 +1,8 @@
#include "models/error.h"
#include "models/verb.h" #include "models/verb.h"
#include <iostream>
const char* obelisk::Verb::createTable() const char* obelisk::Verb::createTable()
{ {
return R"( return R"(
@ -11,7 +14,140 @@ const char* obelisk::Verb::createTable()
)"; )";
} }
int obelisk::Verb::getId() void obelisk::Verb::selectVerb(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 verb 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::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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::DatabaseException::BusyException();
break;
case SQLITE_MISUSE :
throw obelisk::DatabaseException::MisuseException();
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::Verb::insertVerb(sqlite3* dbConnection)
{
if (dbConnection == nullptr)
{
throw obelisk::DatabaseException("database isn't open");
}
sqlite3_stmt* 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);
switch (result)
{
case SQLITE_OK :
break;
case SQLITE_TOOBIG :
throw obelisk::DatabaseException::SizeException();
break;
case SQLITE_RANGE :
throw obelisk::DatabaseException::RangeException();
break;
case SQLITE_NOMEM :
throw obelisk::DatabaseException::MemoryException();
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::DatabaseException::ConstraintException(
sqlite3_errmsg(dbConnection));
case SQLITE_BUSY :
throw obelisk::DatabaseException::BusyException();
break;
case SQLITE_MISUSE :
throw obelisk::DatabaseException::MisuseException();
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::Verb::getId()
{ {
return id_; return id_;
} }
@ -21,7 +157,7 @@ void obelisk::Verb::setId(int id)
id_ = id; id_ = id;
} }
std::string obelisk::Verb::getName() std::string& obelisk::Verb::getName()
{ {
return name_; return name_;
} }

View File

@ -1,6 +1,8 @@
#ifndef OBELISK_MODELS_VERB_H #ifndef OBELISK_MODELS_VERB_H
#define OBELISK_MODELS_VERB_H #define OBELISK_MODELS_VERB_H
#include <sqlite3.h>
#include <string> #include <string>
namespace obelisk namespace obelisk
@ -38,11 +40,14 @@ namespace obelisk
static const char* createTable(); static const char* createTable();
int getId(); int& getId();
void setId(int id); void setId(int id);
std::string getName(); std::string& getName();
void setName(std::string name); void setName(std::string name);
void selectVerb(sqlite3* dbConnection);
void insertVerb(sqlite3* dbConnection);
}; };
} // namespace obelisk } // namespace obelisk

View File

@ -8,9 +8,21 @@
#include <limits> #include <limits>
#include <memory> #include <memory>
static void mainLoop() static int mainLoop()
{ {
auto parser = std::unique_ptr<obelisk::Parser> {new obelisk::Parser()}; auto parser = std::unique_ptr<obelisk::Parser> {new obelisk::Parser()};
std::unique_ptr<obelisk::KnowledgeBase> kb;
try
{
kb = std::unique_ptr<obelisk::KnowledgeBase> {
new obelisk::KnowledgeBase("cromer.kb")};
}
catch (obelisk::KnowledgeBaseException& exception)
{
std::cout << exception.what() << std::endl;
return EXIT_FAILURE;
}
// Prime the first token. // Prime the first token.
fprintf(stderr, "ready> "); fprintf(stderr, "ready> ");
@ -22,7 +34,7 @@ static void mainLoop()
switch (parser->getCurrentToken()) switch (parser->getCurrentToken())
{ {
case obelisk::Lexer::kTokenEof : case obelisk::Lexer::kTokenEof :
return; return EXIT_SUCCESS;
case ';' : // ignore top-level semicolons. case ';' : // ignore top-level semicolons.
std::cout << "Identifier: " std::cout << "Identifier: "
<< parser->getLexer()->getIdentifier() << std::endl; << parser->getLexer()->getIdentifier() << std::endl;
@ -31,19 +43,21 @@ static void mainLoop()
parser->getNextToken(); parser->getNextToken();
break; break;
case obelisk::Lexer::kTokenFact : case obelisk::Lexer::kTokenFact :
// parser->handleFactFunction(); parser->handleFact(kb);
break; break;
case obelisk::Lexer::kTokenRule : case obelisk::Lexer::kTokenRule :
// parser->handleRuleFunction(); // parser->handleRule();
break; break;
case obelisk::Lexer::kTokenAction : case obelisk::Lexer::kTokenAction :
// parser->handleActionFunction(); // parser->handleAction();
break; break;
default : default :
parser->getNextToken(); parser->getNextToken();
break; break;
} }
} }
return EXIT_SUCCESS;
} }
int main(int argc, char** argv) int main(int argc, char** argv)
@ -79,28 +93,5 @@ int main(int argc, char** argv)
return EXIT_FAILURE; return EXIT_FAILURE;
}*/ }*/
try return mainLoop();
{
auto kb = std::unique_ptr<obelisk::KnowledgeBase> {
new obelisk::KnowledgeBase("cromer.kb")};
/*std::vector<std::string> leftObjects;
std::vector<std::string> rightObjects;
leftObjects.push_back("chris");
leftObjects.push_back("martin");
rightObjects.push_back("happy");
rightObjects.push_back("smart");
kb->addFacts("is", leftObjects, rightObjects);*/
}
catch (obelisk::KnowledgeBaseException& exception)
{
std::cout << exception.what() << std::endl;
return EXIT_FAILURE;
}
mainLoop();
return EXIT_SUCCESS;
} }

View File

@ -1 +1 @@
static void mainLoop(); static int mainLoop();

View File

@ -1,10 +1,14 @@
#include "ast/call_expression_ast.h" #include "ast/call_expression_ast.h"
#include "ast/number_expression_ast.h" #include "ast/number_expression_ast.h"
#include "ast/variable_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 "parser.h"
#include <memory> #include <memory>
#include <stack> #include <stack>
#include <string>
#include <vector> #include <vector>
obelisk::Parser::Parser() obelisk::Parser::Parser()
@ -211,64 +215,27 @@ std::unique_ptr<obelisk::PrototypeAST> obelisk::Parser::parseExtern()
return parsePrototype(); return parsePrototype();
} }
//action("martin" is "dangerous" then "avoid" or "ignore");
std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseAction() std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseAction()
{ {
//action(is "dangerous" then "avoid" or "ignore");
getNextToken();
if (getCurrentToken() != '(')
{
// TODO: throw an error
}
} }
//rule("chris" and "martin" is "happy" if "chris" plays "playstation");
std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseRule() std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseRule()
{ {
//rule("player" can "die" if "enemy1" is "dangerous");
getNextToken();
if (getCurrentToken() != '(')
{
// TODO: throw an error
}
while (true) //left side of Rule
{
getNextToken();
if (getCurrentToken() != '"')
{
//TODO: throw an error
}
/*if (getCurrentToken() == ')') // TODO: break if not string and not "and"
{
// TODO: save the verb
break;
}*/
}
while (true) //right side of Ruke
{
getNextToken();
if (getCurrentToken() != '"')
{
//TODO: throw an error
}
if (getCurrentToken() == ')')
{
// TODO: save the verb
break;
}
}
} }
// fact("chris cromer" and "martin" and "Isabella" can "program" and "speak english"); // fact("chris cromer" and "martin" and "Isabella" can "program" and "speak english");
// fact("" and "martin") // fact("" and "martin")
std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseFact() void obelisk::Parser::parseFact(std::vector<obelisk::Fact>& facts)
{ {
std::stack<char> syntax; std::stack<char> syntax;
getNextToken(); getNextToken();
if (getCurrentToken() != '(') if (getCurrentToken() != '(')
{ {
// TODO: throw an error throw obelisk::ParserException(
"expected '(' but got '" + std::to_string(getCurrentToken()) + "'");
} }
syntax.push('('); syntax.push('(');
@ -326,14 +293,29 @@ std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseFact()
{ {
if (getCurrentToken() == ')') if (getCurrentToken() == ')')
{ {
// TODO: throw an error if verb is empty // closing parenthesis found, make sure we have everything needed
// TODO: throw an error if rightEntities has 0 elements if (verb == "")
{
throw obelisk::ParserException("verb is empty");
}
if (leftEntities.size() == 0)
{
throw obelisk::ParserException(
"missing left side entities");
}
if (rightEntities.size() == 0)
{
throw obelisk::ParserException(
"missing right side entities");
}
break; break;
} }
if (getCurrentToken() == '"') if (getCurrentToken() == '"')
{ {
// TODO: throw and error because there is an unexpected double quote. throw obelisk::ParserException("unexpected '\"'");
break; break;
} }
@ -352,22 +334,97 @@ std::unique_ptr<obelisk::ExpressionAST> obelisk::Parser::parseFact()
} }
} }
return nullptr; for (auto& leftEntity : leftEntities)
{
for (auto& rightEntity : rightEntities)
{
facts.push_back(obelisk::Fact(obelisk::Entity(leftEntity),
obelisk::Entity(rightEntity),
obelisk::Verb(verb)));
}
}
} }
void obelisk::Parser::handleAction() void obelisk::Parser::handleAction(std::unique_ptr<obelisk::KnowledgeBase>& kb)
{ {
} }
void obelisk::Parser::handleRule() void obelisk::Parser::handleRule(std::unique_ptr<obelisk::KnowledgeBase>& kb)
{ {
} }
void obelisk::Parser::handleFact() void obelisk::Parser::handleFact(std::unique_ptr<obelisk::KnowledgeBase>& kb)
{ {
parseFact(); std::vector<obelisk::Fact> facts;
parseFact(facts);
int verbId = 0;
for (auto& fact : facts)
{
// TODO: doesn't work after first insert
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)
{
obelisk::Entity entity = fact.getLeftEntity();
kb->getEntity(entity);
if (entity.getId() == 0)
{
// TODO: throw an error here, it was not inserted, and doesn't exist in the database
}
else
{
fact.setLeftEntity(entity);
}
} }
void obelisk::Parser::insertFact() 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)
{
// TODO: throw an error here, it was not inserted, and doesn't exist in the database
} }
else
{
fact.setRightEntity(entity);
}
}
if (verbId == 0)
{
std::vector<obelisk::Verb> verbs = {fact.getVerb()};
kb->addVerbs(verbs);
if (verbs.front().getId() != 0)
{
// The verb was inserted
fact.setVerb(verbs.front());
verbId = fact.getVerb().getId();
}
else
{
// The verb is already already in the knowledge base
// TODO: SELECT the verb and save it into verbId
}
}
else
{
fact.getVerb().setId(verbId);
}
// INSERT INTO fact
std::vector<obelisk::Fact> facts {fact};
kb->addFacts(facts);
fact = facts.front();
}
}
// fact("chris cromer" and "martin" and "Isabella" can "program" and "speak english");

View File

@ -4,7 +4,9 @@
#include "ast/expression_ast.h" #include "ast/expression_ast.h"
#include "ast/function_ast.h" #include "ast/function_ast.h"
#include "ast/prototype_ast.h" #include "ast/prototype_ast.h"
#include "knowledge_base.h"
#include "lexer.h" #include "lexer.h"
#include "models/fact.h"
#include <memory> #include <memory>
@ -34,7 +36,7 @@ namespace obelisk
std::unique_ptr<obelisk::PrototypeAST> parseExtern(); std::unique_ptr<obelisk::PrototypeAST> parseExtern();
std::unique_ptr<obelisk::ExpressionAST> parseAction(); std::unique_ptr<obelisk::ExpressionAST> parseAction();
std::unique_ptr<obelisk::ExpressionAST> parseRule(); std::unique_ptr<obelisk::ExpressionAST> parseRule();
std::unique_ptr<obelisk::ExpressionAST> parseFact(); void parseFact(std::vector<obelisk::Fact>& facts);
public: public:
Parser(); Parser();
@ -48,9 +50,31 @@ namespace obelisk
void handleDefinition(); void handleDefinition();
void handleExtern(); void handleExtern();
void handleTopLevelExpression(); void handleTopLevelExpression();
void handleAction(); void handleAction(std::unique_ptr<obelisk::KnowledgeBase>& kb);
void handleRule(); void handleRule(std::unique_ptr<obelisk::KnowledgeBase>& kb);
void handleFact(); void handleFact(std::unique_ptr<obelisk::KnowledgeBase>& kb);
};
class ParserException : public std::exception
{
private:
const std::string errorMessage_;
public:
ParserException() :
errorMessage_("an unknown error ocurred")
{
}
ParserException(const std::string& errorMessage) :
errorMessage_(errorMessage)
{
}
const char* what() const noexcept
{
return errorMessage_.c_str();
}
}; };
} // namespace obelisk } // namespace obelisk