diff --git a/compile_commands.json b/compile_commands.json index e5cdbba..32f3341 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -1,61 +1,97 @@ [ { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/obelisk.cpp.o -MF src/obelisk.p/obelisk.cpp.o.d -o src/obelisk.p/obelisk.cpp.o -c ../src/obelisk.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/obelisk.cpp.o -MF src/obelisk.p/obelisk.cpp.o.d -o src/obelisk.p/obelisk.cpp.o -c ../src/obelisk.cpp", "file": "../src/obelisk.cpp", "output": "src/obelisk.p/obelisk.cpp.o" }, { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/lexer.cpp.o -MF src/obelisk.p/lexer.cpp.o.d -o src/obelisk.p/lexer.cpp.o -c ../src/lexer.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/lexer.cpp.o -MF src/obelisk.p/lexer.cpp.o.d -o src/obelisk.p/lexer.cpp.o -c ../src/lexer.cpp", "file": "../src/lexer.cpp", "output": "src/obelisk.p/lexer.cpp.o" }, { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/parser.cpp.o -MF src/obelisk.p/parser.cpp.o.d -o src/obelisk.p/parser.cpp.o -c ../src/parser.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/parser.cpp.o -MF src/obelisk.p/parser.cpp.o.d -o src/obelisk.p/parser.cpp.o -c ../src/parser.cpp", "file": "../src/parser.cpp", "output": "src/obelisk.p/parser.cpp.o" }, { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/knowledge_base.cpp.o -MF src/obelisk.p/knowledge_base.cpp.o.d -o src/obelisk.p/knowledge_base.cpp.o -c ../src/knowledge_base.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/knowledge_base.cpp.o -MF src/obelisk.p/knowledge_base.cpp.o.d -o src/obelisk.p/knowledge_base.cpp.o -c ../src/knowledge_base.cpp", "file": "../src/knowledge_base.cpp", "output": "src/obelisk.p/knowledge_base.cpp.o" }, { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/models_action.cpp.o -MF src/obelisk.p/models_action.cpp.o.d -o src/obelisk.p/models_action.cpp.o -c ../src/models/action.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/ast_call_expression_ast.cpp.o -MF src/obelisk.p/ast_call_expression_ast.cpp.o.d -o src/obelisk.p/ast_call_expression_ast.cpp.o -c ../src/ast/call_expression_ast.cpp", + "file": "../src/ast/call_expression_ast.cpp", + "output": "src/obelisk.p/ast_call_expression_ast.cpp.o" + }, + { + "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/ast_error.cpp.o -MF src/obelisk.p/ast_error.cpp.o.d -o src/obelisk.p/ast_error.cpp.o -c ../src/ast/error.cpp", + "file": "../src/ast/error.cpp", + "output": "src/obelisk.p/ast_error.cpp.o" + }, + { + "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/ast_function_ast.cpp.o -MF src/obelisk.p/ast_function_ast.cpp.o.d -o src/obelisk.p/ast_function_ast.cpp.o -c ../src/ast/function_ast.cpp", + "file": "../src/ast/function_ast.cpp", + "output": "src/obelisk.p/ast_function_ast.cpp.o" + }, + { + "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/ast_number_expression_ast.cpp.o -MF src/obelisk.p/ast_number_expression_ast.cpp.o.d -o src/obelisk.p/ast_number_expression_ast.cpp.o -c ../src/ast/number_expression_ast.cpp", + "file": "../src/ast/number_expression_ast.cpp", + "output": "src/obelisk.p/ast_number_expression_ast.cpp.o" + }, + { + "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/ast_prototype_ast.cpp.o -MF src/obelisk.p/ast_prototype_ast.cpp.o.d -o src/obelisk.p/ast_prototype_ast.cpp.o -c ../src/ast/prototype_ast.cpp", + "file": "../src/ast/prototype_ast.cpp", + "output": "src/obelisk.p/ast_prototype_ast.cpp.o" + }, + { + "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/ast_variable_expression_ast.cpp.o -MF src/obelisk.p/ast_variable_expression_ast.cpp.o.d -o src/obelisk.p/ast_variable_expression_ast.cpp.o -c ../src/ast/variable_expression_ast.cpp", + "file": "../src/ast/variable_expression_ast.cpp", + "output": "src/obelisk.p/ast_variable_expression_ast.cpp.o" + }, + { + "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/models_action.cpp.o -MF src/obelisk.p/models_action.cpp.o.d -o src/obelisk.p/models_action.cpp.o -c ../src/models/action.cpp", "file": "../src/models/action.cpp", "output": "src/obelisk.p/models_action.cpp.o" }, { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/models_entity.cpp.o -MF src/obelisk.p/models_entity.cpp.o.d -o src/obelisk.p/models_entity.cpp.o -c ../src/models/entity.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/models_entity.cpp.o -MF src/obelisk.p/models_entity.cpp.o.d -o src/obelisk.p/models_entity.cpp.o -c ../src/models/entity.cpp", "file": "../src/models/entity.cpp", "output": "src/obelisk.p/models_entity.cpp.o" }, { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/models_fact.cpp.o -MF src/obelisk.p/models_fact.cpp.o.d -o src/obelisk.p/models_fact.cpp.o -c ../src/models/fact.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/models_fact.cpp.o -MF src/obelisk.p/models_fact.cpp.o.d -o src/obelisk.p/models_fact.cpp.o -c ../src/models/fact.cpp", "file": "../src/models/fact.cpp", "output": "src/obelisk.p/models_fact.cpp.o" }, { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/models_rule.cpp.o -MF src/obelisk.p/models_rule.cpp.o.d -o src/obelisk.p/models_rule.cpp.o -c ../src/models/rule.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/models_rule.cpp.o -MF src/obelisk.p/models_rule.cpp.o.d -o src/obelisk.p/models_rule.cpp.o -c ../src/models/rule.cpp", "file": "../src/models/rule.cpp", "output": "src/obelisk.p/models_rule.cpp.o" }, { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/models_suggest_action.cpp.o -MF src/obelisk.p/models_suggest_action.cpp.o.d -o src/obelisk.p/models_suggest_action.cpp.o -c ../src/models/suggest_action.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/models_suggest_action.cpp.o -MF src/obelisk.p/models_suggest_action.cpp.o.d -o src/obelisk.p/models_suggest_action.cpp.o -c ../src/models/suggest_action.cpp", "file": "../src/models/suggest_action.cpp", "output": "src/obelisk.p/models_suggest_action.cpp.o" }, { "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", - "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -MD -MQ src/obelisk.p/models_verb.cpp.o -MF src/obelisk.p/models_verb.cpp.o.d -o src/obelisk.p/models_verb.cpp.o -c ../src/models/verb.cpp", + "command": "c++ -Isrc/obelisk.p -Isrc -I../src -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -isystem/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MD -MQ src/obelisk.p/models_verb.cpp.o -MF src/obelisk.p/models_verb.cpp.o.d -o src/obelisk.p/models_verb.cpp.o -c ../src/models/verb.cpp", "file": "../src/models/verb.cpp", "output": "src/obelisk.p/models_verb.cpp.o" } diff --git a/src/ast/ast.h b/src/ast/ast.h new file mode 100644 index 0000000..1002ace --- /dev/null +++ b/src/ast/ast.h @@ -0,0 +1,20 @@ +#ifndef OBELISK_AST_AST_H +#define OBELISK_AST_AST_H + +#include +#include +#include +#include + +#include +#include + +namespace obelisk +{ + static std::unique_ptr TheContext; + static std::unique_ptr TheModule; + static std::unique_ptr> Builder; + static std::map NamedValues; +} // namespace obelisk + +#endif diff --git a/src/ast/call_expression_ast.cpp b/src/ast/call_expression_ast.cpp new file mode 100644 index 0000000..eca1830 --- /dev/null +++ b/src/ast/call_expression_ast.cpp @@ -0,0 +1,31 @@ +#include "ast/ast.h" +#include "ast/call_expression_ast.h" +#include "ast/error.h" + +llvm::Value *obelisk::CallExpressionAST::codegen() +{ + // Look up the name in the global module table. + llvm::Function *calleeF = TheModule->getFunction(callee_); + if (!calleeF) + { + return LogErrorV("Unknown function referenced"); + } + + // If argument mismatch error. + if (calleeF->arg_size() != args_.size()) + { + return LogErrorV("Incorrect # arguments passed"); + } + + std::vector argsV; + for (unsigned i = 0, e = args_.size(); i != e; ++i) + { + argsV.push_back(args_[i]->codegen()); + if (!argsV.back()) + { + return nullptr; + } + } + + return Builder->CreateCall(calleeF, argsV, "calltmp"); +} diff --git a/src/ast/call_expression_ast.h b/src/ast/call_expression_ast.h index 247e0fb..7deaea9 100644 --- a/src/ast/call_expression_ast.h +++ b/src/ast/call_expression_ast.h @@ -28,6 +28,8 @@ namespace obelisk args_(std::move(args)) { } + + llvm::Value *codegen() override; }; } // namespace obelisk diff --git a/src/ast/error.cpp b/src/ast/error.cpp new file mode 100644 index 0000000..779f794 --- /dev/null +++ b/src/ast/error.cpp @@ -0,0 +1,13 @@ +#include "ast/error.h" + +std::unique_ptr obelisk::LogError(const char *Str) +{ + fprintf(stderr, "Error: %s\n", Str); + return nullptr; +} + +llvm::Value *obelisk::LogErrorV(const char *Str) +{ + LogError(Str); + return nullptr; +} diff --git a/src/ast/error.h b/src/ast/error.h new file mode 100644 index 0000000..1770f7f --- /dev/null +++ b/src/ast/error.h @@ -0,0 +1,14 @@ +#ifndef OBELISK_AST_ERROR_H +#define OBELISK_AST_ERROR_H + +#include "ast/expression_ast.h" + +#include + +namespace obelisk +{ + std::unique_ptr LogError(const char *Str); + llvm::Value *LogErrorV(const char *Str); +} // namespace obelisk + +#endif diff --git a/src/ast/expression_ast.h b/src/ast/expression_ast.h index 11ba2d9..a77fff7 100644 --- a/src/ast/expression_ast.h +++ b/src/ast/expression_ast.h @@ -1,12 +1,15 @@ #ifndef OBELISK_AST_EXPRESSION_AST_H #define OBELISK_AST_EXPRESSION_AST_H +#include + namespace obelisk { class ExpressionAST { public: - virtual ~ExpressionAST() = default; + virtual ~ExpressionAST() = default; + virtual llvm::Value *codegen() = 0; }; } // namespace obelisk diff --git a/src/ast/function_ast.cpp b/src/ast/function_ast.cpp new file mode 100644 index 0000000..1bdad9d --- /dev/null +++ b/src/ast/function_ast.cpp @@ -0,0 +1,39 @@ +#include "ast/ast.h" +#include "ast/function_ast.h" + +#include + +llvm::Function *obelisk::FunctionAST::codegen() +{ + llvm::Function *theFunction = TheModule->getFunction(prototype_->getName()); + + if (!theFunction) + { + theFunction = prototype_->codegen(); + } + + if (!theFunction) + { + return nullptr; + } + + llvm::BasicBlock *bB + = llvm::BasicBlock::Create(*TheContext, "entry", theFunction); + Builder->SetInsertPoint(bB); + + NamedValues.clear(); + for (auto &arg : theFunction->args()) + { + NamedValues[std::string(arg.getName())] = &arg; + } + + if (llvm::Value *retVal = body_->codegen()) + { + Builder->CreateRet(retVal); + llvm::verifyFunction(*theFunction); + return theFunction; + } + + theFunction->eraseFromParent(); + return nullptr; +} diff --git a/src/ast/function_ast.h b/src/ast/function_ast.h index 1d4ff4a..bdf630e 100644 --- a/src/ast/function_ast.h +++ b/src/ast/function_ast.h @@ -24,6 +24,8 @@ namespace obelisk body_(std::move(body)) { } + + llvm::Function *codegen(); }; } // namespace obelisk diff --git a/src/ast/meson.build b/src/ast/meson.build index afb5a71..82aa247 100644 --- a/src/ast/meson.build +++ b/src/ast/meson.build @@ -1,8 +1,8 @@ obelisk_ast_sources = files( - 'call_expression_ast.h', - 'expression_ast.h', - 'function_ast.h', - 'number_expression_ast.h', - 'prototype_ast.h', - 'variable_expression_ast.h' + 'call_expression_ast.cpp', + 'error.cpp', + 'function_ast.cpp', + 'number_expression_ast.cpp', + 'prototype_ast.cpp', + 'variable_expression_ast.cpp' ) diff --git a/src/ast/number_expression_ast.cpp b/src/ast/number_expression_ast.cpp new file mode 100644 index 0000000..bff01cb --- /dev/null +++ b/src/ast/number_expression_ast.cpp @@ -0,0 +1,7 @@ +#include "ast/ast.h" +#include "ast/number_expression_ast.h" + +llvm::Value *obelisk::NumberExpressionAST::codegen() +{ + return llvm::ConstantFP::get(*TheContext, llvm::APFloat(number_)); +} diff --git a/src/ast/number_expression_ast.h b/src/ast/number_expression_ast.h index 568e2d6..874cb79 100644 --- a/src/ast/number_expression_ast.h +++ b/src/ast/number_expression_ast.h @@ -18,6 +18,8 @@ namespace obelisk number_(number) { } + + llvm::Value *codegen() override; }; } // namespace obelisk diff --git a/src/ast/prototype_ast.cpp b/src/ast/prototype_ast.cpp new file mode 100644 index 0000000..f7caba4 --- /dev/null +++ b/src/ast/prototype_ast.cpp @@ -0,0 +1,25 @@ +#include "ast/ast.h" +#include "ast/prototype_ast.h" + +llvm::Function *obelisk::PrototypeAST::codegen() +{ + std::vector doubles(args_.size(), + llvm::Type::getDoubleTy(*TheContext)); + llvm::FunctionType *FT + = llvm::FunctionType::get(llvm::Type::getDoubleTy(*TheContext), + doubles, + false); + + llvm::Function *F = llvm::Function::Create(FT, + llvm::Function::ExternalLinkage, + name_, + obelisk::TheModule.get()); + + unsigned idx = 0; + for (auto &arg : F->args()) + { + arg.setName(args_[idx++]); + } + + return F; +} diff --git a/src/ast/prototype_ast.h b/src/ast/prototype_ast.h index c461fb8..5e09b8c 100644 --- a/src/ast/prototype_ast.h +++ b/src/ast/prototype_ast.h @@ -1,6 +1,8 @@ #ifndef OBELISK_AST_PROTOTYPE_AST_H #define OBELISK_AST_PROTOTYPE_AST_H +#include + #include #include @@ -28,6 +30,8 @@ namespace obelisk { return name_; } + + llvm::Function* codegen(); }; } //namespace obelisk diff --git a/src/ast/variable_expression_ast.cpp b/src/ast/variable_expression_ast.cpp new file mode 100644 index 0000000..8923fa3 --- /dev/null +++ b/src/ast/variable_expression_ast.cpp @@ -0,0 +1,13 @@ +#include "ast/ast.h" +#include "ast/error.h" +#include "ast/variable_expression_ast.h" + +llvm::Value *obelisk::VariableExpressionAST::codegen() +{ + llvm::Value *V = NamedValues[name_]; + if (!V) + { + return obelisk::LogErrorV("Unknown variable name"); + } + return V; +} diff --git a/src/ast/variable_expression_ast.h b/src/ast/variable_expression_ast.h index 6089080..3b51f87 100644 --- a/src/ast/variable_expression_ast.h +++ b/src/ast/variable_expression_ast.h @@ -19,6 +19,8 @@ namespace obelisk name_(name) { } + + llvm::Value *codegen() override; }; } //namespace obelisk diff --git a/src/knowledge_base.cpp b/src/knowledge_base.cpp index ce47572..a0209dd 100644 --- a/src/knowledge_base.cpp +++ b/src/knowledge_base.cpp @@ -1,18 +1,16 @@ #include "knowledge_base.h" #include "models/action.h" #include "models/entity.h" +#include "models/error.h" #include "models/fact.h" #include "models/rule.h" #include "models/suggest_action.h" #include "models/verb.h" +#include #include #include - -obelisk::KnowledgeBase::KnowledgeBase(const char* filename) -{ - KnowledgeBase(filename, DEFAULT_FLAGS); -} +#include obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags) { @@ -28,6 +26,8 @@ obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags) logSqliteError(result); } + enableForeignKeys(); + if (!dbExists) { createTable(obelisk::Action::createTable); @@ -47,16 +47,25 @@ obelisk::KnowledgeBase::~KnowledgeBase() } } -void obelisk::KnowledgeBase::createTable(std::function 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; - auto result = sqlite3_exec(dbConnection_, function(), NULL, NULL, &tmp); + char* errmsg; + int result = sqlite3_exec(dbConnection_, + "PRAGMA foreign_keys = ON;", + NULL, + NULL, + &errmsg); if (result != SQLITE_OK) { logSqliteError(result); - if (tmp) + if (errmsg) { - std::string errmsg(tmp); throw obelisk::KnowledgeBaseException(errmsg); } else @@ -66,7 +75,103 @@ void obelisk::KnowledgeBase::createTable(std::function function) } } -// TODO: log files? +void obelisk::KnowledgeBase::createTable(std::function 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& 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& 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& 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) { std::cout << sqlite3_errstr(result) << std::endl; diff --git a/src/knowledge_base.h b/src/knowledge_base.h index 52e742f..7458f65 100644 --- a/src/knowledge_base.h +++ b/src/knowledge_base.h @@ -1,10 +1,15 @@ #ifndef OBELISK_KNOWLEDGE_BASE_H #define OBELISK_KNOWLEDGE_BASE_H +#include "models/entity.h" +#include "models/fact.h" +#include "models/verb.h" + #include #include #include +#include #include namespace obelisk @@ -19,19 +24,27 @@ namespace obelisk int flags_; void logSqliteError(int result); + void enableForeignKeys(); void createTable(std::function function); public: - KnowledgeBase(const char* filename); KnowledgeBase(const char* filename, int flags); + + KnowledgeBase(const char* filename) : + KnowledgeBase(filename, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) + { + } + ~KnowledgeBase(); - template - int addFacts(std::string verb, T leftEntities, U rightEntities); - // TODO: add parameter for fact - template - int addRules(std::string verb, T leftEntities, U rightEntities); - template int addActions(); + void addEntities(std::vector& entities); + void addVerbs(std::vector& verbs); + void addFacts(std::vector& facts); + + void getEntity(obelisk::Entity& entity); + void getVerb(obelisk::Verb& verb); + void getFact(obelisk::Fact& fact); void getDouble(double& result, float var1, float var2); void getFloat(float& result1, float& result2, double var); diff --git a/src/meson.build b/src/meson.build index 0474fe3..0f4d7b2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -13,8 +13,16 @@ 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') + +r = run_command('llvm-config', '--ldflags', '--system-libs', '--libs', 'core', check : true) +link_args = ' ' + r.stdout().replace('\n', ' ') + executable('obelisk', obelisk_sources, dependencies : [sqlite3], + cpp_args : cpp_args.split(), + link_args : link_args.split(), install : true ) diff --git a/src/models/action.cpp b/src/models/action.cpp index 620b6e6..bb6ed06 100644 --- a/src/models/action.cpp +++ b/src/models/action.cpp @@ -11,7 +11,7 @@ const char* obelisk::Action::createTable() )"; } -int obelisk::Action::getId() +int& obelisk::Action::getId() { return id_; } @@ -21,7 +21,7 @@ void obelisk::Action::setId(int id) id_ = id; } -std::string obelisk::Action::getName() +std::string& obelisk::Action::getName() { return name_; } diff --git a/src/models/action.h b/src/models/action.h index d24ba8c..7940f2b 100644 --- a/src/models/action.h +++ b/src/models/action.h @@ -38,10 +38,10 @@ namespace obelisk static const char* createTable(); - int getId(); + int& getId(); void setId(int id); - std::string getName(); + std::string& getName(); void setName(std::string name); }; } // namespace obelisk diff --git a/src/models/entity.cpp b/src/models/entity.cpp index 3345e47..d7e81a8 100644 --- a/src/models/entity.cpp +++ b/src/models/entity.cpp @@ -1,4 +1,5 @@ #include "models/entity.h" +#include "models/error.h" 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_; } @@ -21,7 +157,7 @@ void obelisk::Entity::setId(int id) id_ = id; } -std::string obelisk::Entity::getName() +std::string& obelisk::Entity::getName() { return name_; } diff --git a/src/models/entity.h b/src/models/entity.h index b73f7c9..7e4d0e0 100644 --- a/src/models/entity.h +++ b/src/models/entity.h @@ -1,6 +1,8 @@ #ifndef OBELISK_MODELS_ENTITY_H #define OBELISK_MODELS_ENTITY_H +#include + #include namespace obelisk @@ -38,11 +40,14 @@ namespace obelisk static const char* createTable(); - int getId(); + int& getId(); void setId(int id); - std::string getName(); + std::string& getName(); void setName(std::string name); + + void selectEntity(sqlite3* dbConnection); + void insertEntity(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/models/error.h b/src/models/error.h new file mode 100644 index 0000000..e8ace1b --- /dev/null +++ b/src/models/error.h @@ -0,0 +1,146 @@ +#ifndef OBELISK_MODELS_ERROR_H +#define OBELISK_MODELS_ERROR_H + +#include +#include + +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 diff --git a/src/models/fact.cpp b/src/models/fact.cpp index 132c80b..467373d 100644 --- a/src/models/fact.cpp +++ b/src/models/fact.cpp @@ -1,3 +1,4 @@ +#include "models/error.h" #include "models/fact.h" const char* obelisk::Fact::createTable() @@ -9,6 +10,7 @@ const char* obelisk::Fact::createTable() "right_entity" INTEGER NOT NULL, "verb" 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 @@ -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_; } @@ -26,7 +238,7 @@ void obelisk::Fact::setId(int id) id_ = id; } -obelisk::Entity obelisk::Fact::getLeftEntity() +obelisk::Entity& obelisk::Fact::getLeftEntity() { return leftEntity_; } @@ -36,7 +248,7 @@ void obelisk::Fact::setLeftEntity(obelisk::Entity leftEntity) leftEntity_ = leftEntity; } -obelisk::Entity obelisk::Fact::getRightEntity() +obelisk::Entity& obelisk::Fact::getRightEntity() { return rightEntity_; } @@ -46,7 +258,7 @@ void obelisk::Fact::setRightEntity(obelisk::Entity rightEntity) rightEntity_ = rightEntity; } -obelisk::Verb obelisk::Fact::getVerb() +obelisk::Verb& obelisk::Fact::getVerb() { return verb_; } diff --git a/src/models/fact.h b/src/models/fact.h index 6553058..0bdf0ef 100644 --- a/src/models/fact.h +++ b/src/models/fact.h @@ -2,6 +2,7 @@ #define OBELISK_MODELS_FACT_H #include "models/entity.h" +#include "models/fact.h" #include "models/verb.h" #include @@ -56,17 +57,20 @@ namespace obelisk static const char* createTable(); - int getId(); + int& getId(); void setId(int id); - obelisk::Entity getLeftEntity(); + Entity& getLeftEntity(); void setLeftEntity(obelisk::Entity leftEntity); - obelisk::Entity getRightEntity(); + Entity& getRightEntity(); void setRightEntity(obelisk::Entity leftEntity); - obelisk::Verb getVerb(); + Verb& getVerb(); void setVerb(obelisk::Verb verb); + + void selectFact(sqlite3* dbConnection); + void insertFact(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/models/rule.cpp b/src/models/rule.cpp index 3282743..7dfc4f5 100644 --- a/src/models/rule.cpp +++ b/src/models/rule.cpp @@ -8,13 +8,14 @@ const char* obelisk::Rule::createTable() "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() +int& obelisk::Rule::getId() { return id_; } @@ -24,7 +25,7 @@ void obelisk::Rule::setId(int id) id_ = id; } -obelisk::Fact obelisk::Rule::getFact() +obelisk::Fact& obelisk::Rule::getFact() { return fact_; } @@ -34,7 +35,7 @@ void obelisk::Rule::setFact(obelisk::Fact fact) fact_ = fact; } -obelisk::Fact obelisk::Rule::getReason() +obelisk::Fact& obelisk::Rule::getReason() { return reason_; } diff --git a/src/models/rule.h b/src/models/rule.h index f120896..ae75a85 100644 --- a/src/models/rule.h +++ b/src/models/rule.h @@ -45,13 +45,13 @@ namespace obelisk static const char* createTable(); - int getId(); + int& getId(); void setId(int id); - obelisk::Fact getFact(); + obelisk::Fact& getFact(); void setFact(obelisk::Fact fact); - obelisk::Fact getReason(); + obelisk::Fact& getReason(); void setReason(obelisk::Fact reason); }; } // namespace obelisk diff --git a/src/models/suggest_action.cpp b/src/models/suggest_action.cpp index 1b6c6e6..838a33b 100644 --- a/src/models/suggest_action.cpp +++ b/src/models/suggest_action.cpp @@ -9,6 +9,7 @@ const char* obelisk::SuggestAction::createTable() "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 @@ -16,7 +17,7 @@ const char* obelisk::SuggestAction::createTable() )"; } -int obelisk::SuggestAction::getId() +int& obelisk::SuggestAction::getId() { return id_; } @@ -26,7 +27,7 @@ void obelisk::SuggestAction::setId(int id) id_ = id; } -obelisk::Fact obelisk::SuggestAction::getFact() +obelisk::Fact& obelisk::SuggestAction::getFact() { return fact_; } @@ -36,7 +37,7 @@ void obelisk::SuggestAction::setFact(obelisk::Fact fact) fact_ = fact; } -obelisk::Action obelisk::SuggestAction::getTrueAction() +obelisk::Action& obelisk::SuggestAction::getTrueAction() { return trueAction_; } @@ -46,7 +47,7 @@ void obelisk::SuggestAction::setTrueAction(obelisk::Action trueAction) trueAction_ = trueAction; } -obelisk::Action obelisk::SuggestAction::getFalseAction() +obelisk::Action& obelisk::SuggestAction::getFalseAction() { return falseAction_; } diff --git a/src/models/suggest_action.h b/src/models/suggest_action.h index 6b33e88..2f680e4 100644 --- a/src/models/suggest_action.h +++ b/src/models/suggest_action.h @@ -56,16 +56,16 @@ namespace obelisk static const char* createTable(); - int getId(); + int& getId(); void setId(int id); - obelisk::Fact getFact(); + obelisk::Fact& getFact(); void setFact(obelisk::Fact fact); - obelisk::Action getTrueAction(); + obelisk::Action& getTrueAction(); void setTrueAction(obelisk::Action trueAction); - obelisk::Action getFalseAction(); + obelisk::Action& getFalseAction(); void setFalseAction(obelisk::Action falseAction); }; } // namespace obelisk diff --git a/src/models/verb.cpp b/src/models/verb.cpp index ac9aeb9..9649533 100644 --- a/src/models/verb.cpp +++ b/src/models/verb.cpp @@ -1,5 +1,8 @@ +#include "models/error.h" #include "models/verb.h" +#include + const char* obelisk::Verb::createTable() { 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_; } @@ -21,7 +157,7 @@ void obelisk::Verb::setId(int id) id_ = id; } -std::string obelisk::Verb::getName() +std::string& obelisk::Verb::getName() { return name_; } diff --git a/src/models/verb.h b/src/models/verb.h index dfb3a6e..058d1d1 100644 --- a/src/models/verb.h +++ b/src/models/verb.h @@ -1,6 +1,8 @@ #ifndef OBELISK_MODELS_VERB_H #define OBELISK_MODELS_VERB_H +#include + #include namespace obelisk @@ -38,11 +40,14 @@ namespace obelisk static const char* createTable(); - int getId(); + int& getId(); void setId(int id); - std::string getName(); + std::string& getName(); void setName(std::string name); + + void selectVerb(sqlite3* dbConnection); + void insertVerb(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/obelisk.cpp b/src/obelisk.cpp index 47c695a..7586d95 100644 --- a/src/obelisk.cpp +++ b/src/obelisk.cpp @@ -8,9 +8,21 @@ #include #include -static void mainLoop() +static int mainLoop() { auto parser = std::unique_ptr {new obelisk::Parser()}; + std::unique_ptr kb; + + try + { + kb = std::unique_ptr { + new obelisk::KnowledgeBase("cromer.kb")}; + } + catch (obelisk::KnowledgeBaseException& exception) + { + std::cout << exception.what() << std::endl; + return EXIT_FAILURE; + } // Prime the first token. fprintf(stderr, "ready> "); @@ -22,7 +34,7 @@ static void mainLoop() switch (parser->getCurrentToken()) { case obelisk::Lexer::kTokenEof : - return; + return EXIT_SUCCESS; case ';' : // ignore top-level semicolons. std::cout << "Identifier: " << parser->getLexer()->getIdentifier() << std::endl; @@ -31,19 +43,21 @@ static void mainLoop() parser->getNextToken(); break; case obelisk::Lexer::kTokenFact : - // parser->handleFactFunction(); + parser->handleFact(kb); break; case obelisk::Lexer::kTokenRule : - // parser->handleRuleFunction(); + // parser->handleRule(); break; case obelisk::Lexer::kTokenAction : - // parser->handleActionFunction(); + // parser->handleAction(); break; default : parser->getNextToken(); break; } } + + return EXIT_SUCCESS; } int main(int argc, char** argv) @@ -79,28 +93,5 @@ int main(int argc, char** argv) return EXIT_FAILURE; }*/ - try - { - auto kb = std::unique_ptr { - new obelisk::KnowledgeBase("cromer.kb")}; - - /*std::vector leftObjects; - std::vector 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; + return mainLoop(); } diff --git a/src/obelisk.h b/src/obelisk.h index d964df7..47cbdd0 100644 --- a/src/obelisk.h +++ b/src/obelisk.h @@ -1 +1 @@ -static void mainLoop(); +static int mainLoop(); diff --git a/src/parser.cpp b/src/parser.cpp index c553891..518786b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,10 +1,14 @@ #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 #include +#include #include obelisk::Parser::Parser() @@ -211,64 +215,27 @@ std::unique_ptr obelisk::Parser::parseExtern() return parsePrototype(); } +//action("martin" is "dangerous" then "avoid" or "ignore"); std::unique_ptr 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::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("" and "martin") -std::unique_ptr obelisk::Parser::parseFact() +void obelisk::Parser::parseFact(std::vector& facts) { std::stack syntax; getNextToken(); if (getCurrentToken() != '(') { - // TODO: throw an error + throw obelisk::ParserException( + "expected '(' but got '" + std::to_string(getCurrentToken()) + "'"); } syntax.push('('); @@ -326,14 +293,29 @@ std::unique_ptr obelisk::Parser::parseFact() { if (getCurrentToken() == ')') { - // TODO: throw an error if verb is empty - // TODO: throw an error if rightEntities has 0 elements + // closing parenthesis found, make sure we have everything needed + 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; } if (getCurrentToken() == '"') { - // TODO: throw and error because there is an unexpected double quote. + throw obelisk::ParserException("unexpected '\"'"); break; } @@ -352,22 +334,97 @@ std::unique_ptr 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& kb) { } -void obelisk::Parser::handleRule() +void obelisk::Parser::handleRule(std::unique_ptr& kb) { } -void obelisk::Parser::handleFact() +void obelisk::Parser::handleFact(std::unique_ptr& kb) { - parseFact(); + std::vector facts; + parseFact(facts); + + int verbId = 0; + for (auto& fact : facts) + { + // TODO: doesn't work after first insert + std::vector entities {fact.getLeftEntity()}; + kb->addEntities(entities); + fact.setLeftEntity(entities.front()); + + // the id was not inserted, so check if it exists in the database + if (fact.getLeftEntity().getId() == 0) + { + 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); + } + } + + 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 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 facts {fact}; + kb->addFacts(facts); + fact = facts.front(); + } } -void obelisk::Parser::insertFact() -{ -} +// fact("chris cromer" and "martin" and "Isabella" can "program" and "speak english"); diff --git a/src/parser.h b/src/parser.h index 8738ee1..a71358e 100644 --- a/src/parser.h +++ b/src/parser.h @@ -4,7 +4,9 @@ #include "ast/expression_ast.h" #include "ast/function_ast.h" #include "ast/prototype_ast.h" +#include "knowledge_base.h" #include "lexer.h" +#include "models/fact.h" #include @@ -34,7 +36,7 @@ namespace obelisk std::unique_ptr parseExtern(); std::unique_ptr parseAction(); std::unique_ptr parseRule(); - std::unique_ptr parseFact(); + void parseFact(std::vector& facts); public: Parser(); @@ -48,9 +50,31 @@ namespace obelisk void handleDefinition(); void handleExtern(); void handleTopLevelExpression(); - void handleAction(); - void handleRule(); - void handleFact(); + void handleAction(std::unique_ptr& kb); + void handleRule(std::unique_ptr& kb); + void handleFact(std::unique_ptr& 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