first proposal of compile process
This commit is contained in:
parent
7d8c5a7f7e
commit
6eac9a5ac5
@ -6,6 +6,7 @@ namespace config
|
||||
{
|
||||
|
||||
constexpr const char * COMPILE_CPU_PATH = "compile_cpu_path";
|
||||
constexpr const char * TEMPLATE_CPU_PATH = "template_cpu_path";
|
||||
constexpr const char * TEMPLATE_CPU_CPP_PATH = "template_cpu_cpp_path";
|
||||
constexpr const char * TEMPLATE_CPU_HPP_PATH = "template_cpu_hpp_path";
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,9 @@
|
||||
#include "cypher/common.hpp"
|
||||
#include "utils/terminate_handler.hpp"
|
||||
|
||||
// dumping ground for all kind of visitors
|
||||
#include "query_engine/traverser/create_traverser.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
@ -30,8 +33,10 @@ int main(int argc, char *argv[])
|
||||
// traversers
|
||||
auto traverser = get_argument(arguments, "-t", "code");
|
||||
auto print_traverser = Traverser::sptr(new PrintVisitor(cout));
|
||||
auto create_traverser = Traverser::sptr(new CreateTraverser());
|
||||
std::map<std::string, Traverser::sptr> traversers = {
|
||||
{"print", print_traverser},
|
||||
{"create", create_traverser}
|
||||
};
|
||||
|
||||
cypher::Compiler compiler;
|
||||
|
@ -7,9 +7,13 @@
|
||||
template<typename T>
|
||||
class DynamicLib
|
||||
{
|
||||
private:
|
||||
using produce_t = typename T::produce;
|
||||
using destruct_t = typename T::destruct;
|
||||
|
||||
public:
|
||||
typename T::produce produce_method;
|
||||
typename T::destruct destruct_method;
|
||||
produce_t produce_method;
|
||||
destruct_t destruct_method;
|
||||
|
||||
DynamicLib(const std::string& lib_path) :
|
||||
lib_path(lib_path)
|
||||
@ -20,8 +24,12 @@ public:
|
||||
typename T::lib_object* instance()
|
||||
{
|
||||
// TODO singleton, lazy, concurrency
|
||||
this->load();
|
||||
lib_object = this->produce_method();
|
||||
// ifs are uncommented -> SEGMENTATION FAULT
|
||||
// TODO debug
|
||||
// if (dynamic_lib == nullptr)
|
||||
this->load();
|
||||
// if (dynamic_lib == nullptr)
|
||||
lib_object = this->produce_method();
|
||||
return lib_object;
|
||||
}
|
||||
|
||||
@ -55,7 +63,7 @@ private:
|
||||
|
||||
void load_produce_func()
|
||||
{
|
||||
produce_method = (typename T::produce) dlsym(
|
||||
produce_method = (produce_t) dlsym(
|
||||
dynamic_lib,
|
||||
T::produce_name.c_str()
|
||||
);
|
||||
@ -67,7 +75,7 @@ private:
|
||||
|
||||
void load_destruct_func()
|
||||
{
|
||||
destruct_method = (typename T::destruct) dlsym(
|
||||
destruct_method = (destruct_t) dlsym(
|
||||
dynamic_lib,
|
||||
T::destruct_name.c_str()
|
||||
);
|
||||
|
@ -17,6 +17,8 @@ public:
|
||||
"-I../",
|
||||
"-shared -fPIC"
|
||||
);
|
||||
|
||||
// synchronous call
|
||||
system(compile_command.c_str());
|
||||
}
|
||||
};
|
||||
|
@ -6,17 +6,22 @@
|
||||
#include "database/db.hpp"
|
||||
#include "utils/log/logger.hpp"
|
||||
|
||||
// preparations before execution
|
||||
// execution
|
||||
// postprocess the results
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
class CodeExecutor
|
||||
{
|
||||
public:
|
||||
|
||||
void execute(ICodeCPU *code_cpu)
|
||||
{
|
||||
log.info(code_cpu->query());
|
||||
code_cpu->run(db);
|
||||
auto result = code_cpu->run(db);
|
||||
cout << result->result << endl;
|
||||
}
|
||||
|
||||
private:
|
||||
Db db;
|
||||
Logger log;
|
||||
};
|
||||
|
@ -3,31 +3,48 @@
|
||||
#include "utils/string/file.hpp"
|
||||
#include "template_engine/engine.hpp"
|
||||
#include "config/config.hpp"
|
||||
#include "utils/log/logger.hpp"
|
||||
#include "traverser/query_traverser.hpp"
|
||||
|
||||
using std::string;
|
||||
|
||||
class CodeGenerator
|
||||
{
|
||||
public:
|
||||
void generate(const std::string& query, const std::string& path)
|
||||
void generate_hpp(const uint64_t stripped_hash, const std::string& path)
|
||||
{
|
||||
string template_path =
|
||||
config::Config::instance()[config::TEMPLATE_CPU_PATH];
|
||||
string template_path = CONFIG(config::TEMPLATE_CPU_HPP_PATH);
|
||||
string template_file = utils::read_file(template_path.c_str());
|
||||
// TODO instead of code should be generated code
|
||||
// use query visitor to build the code
|
||||
string generated = template_engine.render(
|
||||
template_file,
|
||||
{
|
||||
{"class_name", "Code"},
|
||||
{"query", "\"" + query + "\""},
|
||||
{"code", "cout << \"test code\" << endl;"}
|
||||
{"stripped_hash", std::to_string(stripped_hash)},
|
||||
{"data_type", "int"}
|
||||
}
|
||||
);
|
||||
utils::write_file(generated, path);
|
||||
}
|
||||
|
||||
void generate_cpp(const std::string& query,
|
||||
const uint64_t stripped_hash,
|
||||
const std::string& path)
|
||||
{
|
||||
string template_path = CONFIG(config::TEMPLATE_CPU_CPP_PATH);
|
||||
string template_file = utils::read_file(template_path.c_str());
|
||||
traverser.build_tree(query);
|
||||
string code = traverser.traverse();
|
||||
string generated = template_engine.render(
|
||||
template_file,
|
||||
{
|
||||
{"class_name", "CodeCPU"},
|
||||
{"stripped_hash", std::to_string(stripped_hash)},
|
||||
{"query", query},
|
||||
{"code", code},
|
||||
{"return_type", "int"}
|
||||
}
|
||||
);
|
||||
utils::write_file(generated, path);
|
||||
}
|
||||
private:
|
||||
template_engine::TemplateEngine template_engine;
|
||||
Logger log;
|
||||
QueryTraverser traverser;
|
||||
};
|
||||
|
@ -4,18 +4,21 @@
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
// #define NOT_LOG_INFO
|
||||
|
||||
#include "memgraph_dynamic_lib.hpp"
|
||||
#include "query_stripper.hpp"
|
||||
#include "code_compiler.hpp"
|
||||
#include "code_generator.hpp"
|
||||
#include "utils/hashing/fnv.hpp"
|
||||
#include "utils/log/logger.hpp"
|
||||
#include "config/config.hpp"
|
||||
#include "utils/log/logger.hpp"
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
|
||||
class CodeLoader
|
||||
{
|
||||
public:
|
||||
@ -30,10 +33,10 @@ public:
|
||||
ICodeCPU* load_code_cpu(const string& query)
|
||||
{
|
||||
auto stripped = stripper.strip(query);
|
||||
log.info("stripped_query=" + stripped);
|
||||
LOG_INFO("stripped_query=" + stripped);
|
||||
auto stripped_hash = fnv(stripped);
|
||||
auto hash_string = std::to_string(stripped_hash);
|
||||
log.info("query_hash=" + hash_string);
|
||||
LOG_INFO("query_hash=" + hash_string);
|
||||
|
||||
auto code_lib_iter = code_libs.find(stripped_hash);
|
||||
|
||||
@ -47,7 +50,7 @@ public:
|
||||
// TODO load output path from config
|
||||
auto base_path = config::Config::instance()[config::COMPILE_CPU_PATH];
|
||||
auto path_cpp = base_path + hash_string + ".cpp";
|
||||
code_generator.generate(query, path_cpp);
|
||||
code_generator.generate_cpp(query, stripped_hash, path_cpp);
|
||||
|
||||
// TODO compile generated code
|
||||
auto path_so = base_path + hash_string + ".so";
|
||||
@ -71,7 +74,6 @@ private:
|
||||
|
||||
CodeGenerator code_generator;
|
||||
CodeCompiler code_compiler;
|
||||
Logger log;
|
||||
|
||||
sptr_code_lib load_code_lib(const string& path)
|
||||
{
|
||||
|
@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# TODO: create Makefile or cmake script
|
||||
cd compiled/cpu
|
||||
clang++ -std=c++1y create_return.cpp -o create_return.so -I../../../ -shared -fPIC
|
||||
cd ../..
|
||||
# cd compiled/cpu
|
||||
# clang++ -std=c++1y create_return.cpp -o create_return.so -I../../../ -shared -fPIC
|
||||
# cd ../..
|
||||
# clang++ -std=c++1y -g -I../ -I ../lib/yaml-cpp/include main.cpp ../cypher/cypher.cpp -o engine -L ../lib/yaml-cpp/build -l yaml-cpp -l dl
|
||||
clang++ -std=c++1y -g -I../ main.cpp ../cypher/cypher.cpp -o engine -l dl
|
||||
clang++ -std=c++1y -g -I../ main.cpp ../cypher/cypher.cpp -o engine.out -l dl -pthread
|
||||
|
@ -1,34 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query_engine/i_code_cpu.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
class CreateReturn : public ICodeCPU
|
||||
{
|
||||
public:
|
||||
|
||||
std::string query() const override
|
||||
{
|
||||
return "CRETE RETURN QUERY";
|
||||
}
|
||||
|
||||
void run(Db& db) const override
|
||||
{
|
||||
cout << db.identifier() << endl;
|
||||
}
|
||||
|
||||
~CreateReturn() {}
|
||||
};
|
||||
|
||||
extern "C" ICodeCPU* produce()
|
||||
{
|
||||
return new CreateReturn();
|
||||
}
|
||||
|
||||
extern "C" void destruct(ICodeCPU* p)
|
||||
{
|
||||
delete p;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
# Memgraph test config
|
||||
|
||||
compile_cpu_path: ./compiled/cpu/
|
Binary file not shown.
@ -1,16 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "query_engine/query_result.hpp"
|
||||
#include "database/db.hpp"
|
||||
|
||||
class ICodeCPU
|
||||
{
|
||||
public:
|
||||
virtual std::string query() const = 0;
|
||||
virtual void run(Db& db) const = 0;
|
||||
virtual QueryResult::sptr run(Db& db) = 0;
|
||||
virtual ~ICodeCPU() {}
|
||||
};
|
||||
|
||||
typedef ICodeCPU* (*produce_t)();
|
||||
typedef void (*destruct_t)(ICodeCPU*);
|
||||
using produce_t = ICodeCPU*(*)();
|
||||
using destruct_t = void (*)(ICodeCPU*);
|
||||
|
@ -1,9 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class QueryResult
|
||||
{
|
||||
public:
|
||||
using sptr = std::shared_ptr<QueryResult>;
|
||||
|
||||
QueryResult(std::string result) :
|
||||
result(result) {}
|
||||
|
||||
std::string result;
|
||||
};
|
||||
|
@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "cypher/ast/ast.hpp"
|
||||
#include "cypher/compiler.hpp"
|
||||
#include "traverser/node_traverser.hpp"
|
||||
|
||||
// The purpose of this class is to find out has
|
||||
// the query already been compiled into the machine code / c++ code
|
||||
// in the same pass query arguments should be extracted (e.g. poperties
|
||||
// or node names)
|
||||
|
||||
// if the query has already been comiled into the machine code
|
||||
// than it shouldn't be compiled again
|
||||
|
||||
class QueryTraverser
|
||||
{
|
||||
public:
|
||||
QueryTraverser() = default;
|
||||
|
||||
void build_tree(const std::string& query)
|
||||
{
|
||||
tree = compiler.syntax_tree(query);
|
||||
}
|
||||
|
||||
void traverse()
|
||||
{
|
||||
tree.root->accept(traverser);
|
||||
for (auto& kv : traverser.json) {
|
||||
cout << "Key: " << kv.first << ", Value: " << kv.second << endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ast::Ast tree;
|
||||
cypher::Compiler compiler;
|
||||
NodeTraverser traverser;
|
||||
};
|
@ -3,26 +3,26 @@
|
||||
|
||||
#include "query_engine/i_code_cpu.hpp"
|
||||
|
||||
// TODO generate with the template engine
|
||||
// #include "storage/model/properties/jsonwriter.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
// query: {{query}}
|
||||
|
||||
class {{class_name}} : public ICodeCPU
|
||||
{
|
||||
public:
|
||||
|
||||
std::string query() const override
|
||||
QueryResult::sptr run(Db& db) override
|
||||
{
|
||||
return {{query}};
|
||||
}
|
||||
|
||||
void run(Db& db) const override
|
||||
{
|
||||
{{code}}
|
||||
}
|
||||
{{code}} }
|
||||
|
||||
~{{class_name}}() {}
|
||||
};
|
||||
|
||||
|
||||
extern "C" ICodeCPU* produce()
|
||||
{
|
||||
return new {{class_name}}();
|
||||
|
9
query_engine/template/template_code_cpu.hpp
Normal file
9
query_engine/template/template_code_cpu.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "query_engine/i_code_cpu.hpp"
|
||||
|
||||
class ICodeCPUData : ICodeCPU
|
||||
{
|
||||
public:
|
||||
{{data_type}} data;
|
||||
};
|
41
query_engine/traverser/create_traverser.hpp
Normal file
41
query_engine/traverser/create_traverser.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
#include <map>
|
||||
|
||||
#include "storage/model/properties/properties.hpp"
|
||||
#include "storage/model/properties/jsonwriter.hpp"
|
||||
#include "cypher/visitor/traverser.hpp"
|
||||
#include "node_traverser.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
class CreateTraverser : public Traverser
|
||||
{
|
||||
public:
|
||||
std::string code;
|
||||
Properties properties;
|
||||
|
||||
void visit(ast::Create& create) override
|
||||
{
|
||||
code = "\t\tauto& t = db.tx_engine.begin();\n";
|
||||
code += "\t\tauto vertex_accessor = db.graph.vertices.insert(t);\n";
|
||||
Traverser::visit(create);
|
||||
};
|
||||
|
||||
void visit(ast::Node& node) override
|
||||
{
|
||||
Traverser::visit(node);
|
||||
}
|
||||
|
||||
void visit(ast::Return& ret) override
|
||||
{
|
||||
code += "\t\tauto properties = vertex_accessor.properties();\n";
|
||||
code += "\t\tStringBuffer buffer;\n";
|
||||
code += "\t\tJsonWriter<StringBuffer> writer(buffer);\n";
|
||||
code += "\t\tproperties.accept(writer);\n";
|
||||
code += "\t\treturn std::make_shared<QueryResult>(buffer.str());\n";
|
||||
}
|
||||
};
|
@ -1,59 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
#include <map>
|
||||
|
||||
#include "cypher/visitor/traverser.hpp"
|
||||
#include "storage/model/properties/properties.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
class NodeTraverser : public Traverser
|
||||
struct NodeTraverser : public Traverser
|
||||
{
|
||||
struct PropertiesTraverser : public Traverser
|
||||
Properties traverse(ast::Node& node)
|
||||
{
|
||||
PropertiesTraverser(NodeTraverser* node_traverser)
|
||||
: node_traverser(node_traverser) {}
|
||||
Traverser::visit(node);
|
||||
return properties;
|
||||
}
|
||||
|
||||
// friend NodeTraverser;
|
||||
|
||||
void visit(ast::Property& property) override
|
||||
{
|
||||
name = property.idn->name;
|
||||
Traverser::visit(property);
|
||||
json[name] = value;
|
||||
}
|
||||
|
||||
void visit(ast::String& string) override
|
||||
{
|
||||
value = string.value;
|
||||
}
|
||||
|
||||
void visit(ast::Integer& integer) override
|
||||
{
|
||||
value = std::to_string(integer.value);
|
||||
}
|
||||
|
||||
void visit(ast::Node& node) override
|
||||
{
|
||||
Traverser::visit(node);
|
||||
node_traverser->json = json;
|
||||
}
|
||||
private:
|
||||
std::string name;
|
||||
std::string value;
|
||||
std::map<std::string, std::string> json;
|
||||
NodeTraverser* node_traverser;
|
||||
};
|
||||
|
||||
public:
|
||||
// TODO: replace with generic value
|
||||
std::map<std::string, std::string> json;
|
||||
|
||||
void visit(ast::Create& create) override
|
||||
void visit(ast::Property& property) override
|
||||
{
|
||||
auto create_nodes = PropertiesTraverser(this);
|
||||
create.accept(create_nodes);
|
||||
};
|
||||
key = property.idn->name;
|
||||
Traverser::visit(property);
|
||||
if (flag == Property::Flags::Int32) {
|
||||
properties.set<Int32>(key, *int_value);
|
||||
}
|
||||
if (flag == Property::Flags::String) {
|
||||
properties.set<String>(key, *string_value);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(ast::String& string) override
|
||||
{
|
||||
flag = Property::Flags::String;
|
||||
string_value = std::make_shared<String>(string.value);
|
||||
}
|
||||
|
||||
void visit(ast::Integer& integer) override
|
||||
{
|
||||
flag = Property::Flags::Int32;
|
||||
int_value = std::make_shared<Int32>(integer.value);
|
||||
}
|
||||
|
||||
void visit(ast::Node& node) override
|
||||
{
|
||||
Traverser::visit(node);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string key;
|
||||
Property::Flags flag;
|
||||
std::shared_ptr<Int32> int_value;
|
||||
std::shared_ptr<String> string_value;
|
||||
Properties properties;
|
||||
};
|
||||
|
27
query_engine/traverser/query_traverser.hpp
Normal file
27
query_engine/traverser/query_traverser.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "cypher/ast/ast.hpp"
|
||||
#include "cypher/compiler.hpp"
|
||||
#include "create_traverser.hpp"
|
||||
|
||||
class QueryTraverser
|
||||
{
|
||||
public:
|
||||
QueryTraverser() = default;
|
||||
|
||||
void build_tree(const std::string& query)
|
||||
{
|
||||
tree = compiler.syntax_tree(query);
|
||||
}
|
||||
|
||||
std::string traverse()
|
||||
{
|
||||
tree.root->accept(traverser);
|
||||
return traverser.code;
|
||||
}
|
||||
|
||||
private:
|
||||
ast::Ast tree;
|
||||
cypher::Compiler compiler;
|
||||
CreateTraverser traverser;
|
||||
};
|
@ -53,6 +53,7 @@ public:
|
||||
if(first)
|
||||
first = false;
|
||||
}
|
||||
handler.finish();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -45,9 +45,12 @@ public:
|
||||
if (0 == std::strcmp(key, "compile_cpu_path"))
|
||||
return "./compiled/cpu/";
|
||||
|
||||
if (std::strcmp(key, "template_cpu_path") == 0)
|
||||
if (std::strcmp(key, "template_cpu_cpp_path") == 0)
|
||||
return "./template/template_code_cpu.cpp";
|
||||
|
||||
if (std::strcmp(key, "template_cpu_hpp_path") == 0)
|
||||
return "./template/template_code_cpu.hpp";
|
||||
|
||||
throw std::runtime_error("implement me");
|
||||
|
||||
// TODO optimize access
|
||||
@ -56,3 +59,5 @@ public:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define CONFIG(_KEY_) config::Config::instance()[_KEY_]
|
||||
|
@ -8,6 +8,10 @@
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
Logger(Logger& other) = delete;
|
||||
Logger(Logger&& other) = delete;
|
||||
|
||||
private:
|
||||
Logger() = default;
|
||||
|
||||
// TODO logger name support
|
||||
@ -16,13 +20,17 @@ public:
|
||||
|
||||
// TODO handlers support:
|
||||
// * log format support
|
||||
|
||||
void info(const std::string& text)
|
||||
|
||||
// TODO merge with debug/log.hpp
|
||||
|
||||
public:
|
||||
static Logger& instance()
|
||||
{
|
||||
stdout_log(text);
|
||||
static Logger logger;
|
||||
return logger;
|
||||
}
|
||||
|
||||
void debug(const std::string& text)
|
||||
void info(const std::string& text)
|
||||
{
|
||||
stdout_log(text);
|
||||
}
|
||||
@ -35,3 +43,11 @@ private:
|
||||
<< text << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef NOT_LOG_INFO
|
||||
# define LOG_INFO(_)
|
||||
#else
|
||||
# define LOG_INFO(_MESSAGE_) Logger::instance().info(_MESSAGE_);
|
||||
#endif
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user