diff --git a/meson.build b/meson.build index bfea405..2320cb1 100644 --- a/meson.build +++ b/meson.build @@ -23,6 +23,8 @@ snake_lib = static_library('snake', 'src/State.cpp', 'src/DAG.cpp', 'src/SymTable.cpp', + 'src/Loader.cpp', + 'src/Executor.cpp', ], dependencies: [ dependency('sqlite3') @@ -46,6 +48,7 @@ executable('snake-tests', 'tests/main.cpp', 'tests/Lexer.cpp', 'tests/Parser.cpp', + 'tests/Interpreter.cpp', ], dependencies: [ snake_dep, diff --git a/src/Executor.cpp b/src/Executor.cpp new file mode 100644 index 0000000..06ad1c2 --- /dev/null +++ b/src/Executor.cpp @@ -0,0 +1,30 @@ +#include "Executor.hpp" + +namespace sn +{ + /*explicit*/ Executor::Executor() + { + } + + /*virtual*/ Executor::~Executor() + { + } + + /*virtual*/ std::string Executor::execute(std::string const& command) + { + std::stringstream ss; + + FILE* out = popen(command.c_str(), "r"); + assert(out); + + char buf; + + while ( fread(&buf, sizeof(char), 1, out) ) + { + ss << buf; + } + + pclose(out); + return ss.str(); + } +} diff --git a/src/Executor.hpp b/src/Executor.hpp new file mode 100644 index 0000000..a99ee03 --- /dev/null +++ b/src/Executor.hpp @@ -0,0 +1,20 @@ +#ifndef sn_EXECUTOR_HPP +#define sn_EXECUTOR_HPP + +#include "commons.hpp" + +namespace sn +{ + class Executor + { + public: + explicit Executor(); + virtual ~Executor(); + + virtual std::string execute(std::string const& command); + + private: + }; +} + +#endif diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp index 1389a5f..61b50ad 100644 --- a/src/Interpreter.cpp +++ b/src/Interpreter.cpp @@ -5,7 +5,12 @@ namespace sn { - /*explicit*/ Interpreter::Interpreter() + /*explicit*/ Interpreter::Interpreter(std::shared_ptr state, + std::shared_ptr loader, + std::shared_ptr executor) + : m_state { state } + , m_loader { loader } + , m_executor { executor } { } @@ -17,7 +22,7 @@ namespace sn { // Process the document ast // ------------------------ - auto node = parse_snakefile(); + auto node = m_loader->parse_snakefile(); process(node); // Get all files @@ -47,11 +52,9 @@ namespace sn // Get modified files // ------------------ - auto state = std::make_shared(); + m_state->init(files); - state->init(files); - - auto modified_files = state->get_modified_files(files); + auto modified_files = m_state->get_modified_files(files); // Build DAG // --------- @@ -120,7 +123,7 @@ namespace sn } } - state->update(file); + m_state->update(file); } if (sorted.empty()) @@ -129,38 +132,6 @@ namespace sn } } - std::filesystem::path Interpreter::find_snakefile() - { - auto path = std::filesystem::current_path() / "Snakefile"; - - if (!std::filesystem::exists(path)) - { - throw run_error {"Snakefile not found"}; - } - - return path; - } - - std::string Interpreter::load_snakefile() - { - std::ifstream file {find_snakefile()}; - assert(file); - std::stringstream ss; - ss << file.rdbuf(); - return ss.str(); - } - - std::shared_ptr Interpreter::parse_snakefile() - { - Lexer lexer; - lexer.scan(load_snakefile()); - - Parser parser; - auto node = parser.parse(lexer.all()); - - return node; - } - std::string Interpreter::get_value(std::shared_ptr node) { if (node->type() == NODE_IDENT) @@ -283,20 +254,7 @@ namespace sn std::string Interpreter::execute(std::string const& script) const { - std::stringstream ss; - - FILE* out = popen(script.c_str(), "r"); - assert(out); - - char buf; - - while ( fread(&buf, sizeof(char), 1, out) ) - { - ss << buf; - } - - pclose(out); - return ss.str(); + return m_executor->execute(script); } std::filesystem::path diff --git a/src/Interpreter.hpp b/src/Interpreter.hpp index 27b9dd2..3385cc6 100644 --- a/src/Interpreter.hpp +++ b/src/Interpreter.hpp @@ -6,6 +6,8 @@ #include "State.hpp" #include "DAG.hpp" #include "SymTable.hpp" +#include "Loader.hpp" +#include "Executor.hpp" namespace sn { @@ -19,16 +21,18 @@ namespace sn class Interpreter { public: - explicit Interpreter(); + explicit Interpreter(std::shared_ptr state, + std::shared_ptr loader, + std::shared_ptr executor); virtual ~Interpreter(); void run(); - std::filesystem::path find_snakefile(); - std::string load_snakefile(); - std::shared_ptr parse_snakefile(); - private: + std::shared_ptr m_state; + std::shared_ptr m_loader; + std::shared_ptr m_executor; + std::unordered_map> m_dependencies; diff --git a/src/Loader.cpp b/src/Loader.cpp new file mode 100644 index 0000000..94df384 --- /dev/null +++ b/src/Loader.cpp @@ -0,0 +1,46 @@ +#include "Loader.hpp" +#include "Lexer.hpp" +#include "Parser.hpp" + +namespace sn +{ + /*explicit*/ Loader::Loader() + { + } + + /*virtual*/ Loader::~Loader() + { + } + + std::filesystem::path Loader::find_snakefile() + { + auto path = std::filesystem::current_path() / "Snakefile"; + + if (!std::filesystem::exists(path)) + { + throw load_error {"Snakefile not found"}; + } + + return path; + } + + std::string Loader::load_snakefile() + { + std::ifstream file {find_snakefile()}; + assert(file); + std::stringstream ss; + ss << file.rdbuf(); + return ss.str(); + } + + std::shared_ptr Loader::parse_snakefile() + { + Lexer lexer; + lexer.scan(load_snakefile()); + + Parser parser; + auto node = parser.parse(lexer.all()); + + return node; + } +} diff --git a/src/Loader.hpp b/src/Loader.hpp new file mode 100644 index 0000000..eab91f5 --- /dev/null +++ b/src/Loader.hpp @@ -0,0 +1,30 @@ +#ifndef sn_LOADER_HPP +#define sn_LOADER_HPP + +#include "commons.hpp" +#include "Node.hpp" + +namespace sn +{ + SN_ERROR(load_error); + + /** + * Find and compile snakefiles. + * @see Lexer + * @see Parser + **/ + class Loader + { + public: + explicit Loader(); + virtual ~Loader(); + + virtual std::filesystem::path find_snakefile(); + virtual std::string load_snakefile(); + virtual std::shared_ptr parse_snakefile(); + + private: + }; +} + +#endif diff --git a/src/State.hpp b/src/State.hpp index 264b000..9e7c466 100644 --- a/src/State.hpp +++ b/src/State.hpp @@ -22,10 +22,10 @@ namespace sn explicit State(); virtual ~State(); - void init(std::vector const& files); - void update(std::filesystem::path path); + virtual void init(std::vector const& files); + virtual void update(std::filesystem::path path); - std::vector + virtual std::vector get_modified_files(std::vector const& files); std::filesystem::path format_path(std::filesystem::path path) const; diff --git a/src/main.cpp b/src/main.cpp index 1500ad8..5d2a7d9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,10 +3,15 @@ #include "snake.hpp" #include "Interpreter.hpp" #include "State.hpp" +#include "Executor.hpp" int main(int, char**) { - sn::Interpreter interpreter; + auto state = std::make_shared(); + auto loader = std::make_shared(); + auto executor = std::make_shared(); + + sn::Interpreter interpreter {state, loader, executor}; interpreter.run(); return 0; diff --git a/tests/Interpreter.cpp b/tests/Interpreter.cpp new file mode 100644 index 0000000..50c24e6 --- /dev/null +++ b/tests/Interpreter.cpp @@ -0,0 +1,103 @@ +#include +#include "../src/Interpreter.hpp" + +#define TEST_RUN(SRC, ...) \ + test_run(SRC, {__VA_ARGS__}) + +using namespace sn; + +struct StateMock: public State { + std::vector modified_files; + + StateMock(std::vector const& p_modified_files) + : modified_files { p_modified_files } + { + } + + virtual void init(std::vector const&) override + { + } + + virtual void update(std::filesystem::path) override + { + } + + virtual std::vector + get_modified_files(std::vector const&) override + { + return modified_files; + } +}; + +struct LoaderMock: public Loader { + std::string snakefile; + + LoaderMock(std::string const& p_snakefile) + : snakefile { p_snakefile } + { + } + + virtual std::filesystem::path find_snakefile() override { return ""; } + + virtual std::string load_snakefile() override + { + return snakefile; + } +}; + +struct ExecutorMock: public Executor { + std::vector history; + + virtual std::string execute(std::string const& command) override + { + history.push_back(command); + return ""; + } +}; + +class InterpreterTest +{ +public: + explicit InterpreterTest() {} + virtual ~InterpreterTest() {} + + std::vector + test_run(std::string const& snakefile, + std::vector const& modified) + { + auto state = std::make_shared(modified); + + auto loader = std::make_shared(snakefile); + auto executor = std::make_shared(); + + Interpreter interpreter {state, loader, executor}; + interpreter.run(); + + return executor->history; + } + + void test_contains(std::string const& str, std::vector vec) + { + INFO("'" << str << "' not found"); + REQUIRE(std::find(std::begin(vec), std::end(vec), str) + != std::end(vec)); + } + +protected: +}; + +TEST_CASE_METHOD(InterpreterTest, "Interpreter_simple_var") +{ + auto modified = std::vector { + std::filesystem::path("a"), + }; + + std::stringstream ss; + ss << "$X = a" << std::endl; + ss << "b -> $X {" << std::endl; + ss << "executed" << std::endl; + ss << "}" << std::endl; + + auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); + test_contains("executed", res); +}