Compare commits

..

No commits in common. "91a7f61c42cbf3617e8f5a0399e5d3fb009c8a49" and "e862da900064ce80d9219b4bcc190ba3b237dc9b" have entirely different histories.

20 changed files with 71 additions and 488 deletions

View File

@ -1,10 +1,7 @@
DOC ::= (VAR_DECL | RULE)* DOC ::= RULE*
RULE ::= TARGET rarrow DEPS BLOCK RULE ::= TARGET rarrow DEPS BLOCK
TARGET ::= LITERAL+ TARGET ::= ident+
DEPS ::= LITERAL* DEPS ::= ident*
BLOCK ::= obrace CMD_LST cbrace BLOCK ::= obrace CMD_LST cbrace
CMD_LST ::= (CMD (comma CMD)* comma?)? CMD_LST ::= (CMD (comma CMD)* comma?)?
CMD ::= LITERAL* CMD ::= ident*
LITERAL ::= ident | var
VAR_DECL ::= var assign ident

View File

@ -22,9 +22,6 @@ snake_lib = static_library('snake',
'src/Interpreter.cpp', 'src/Interpreter.cpp',
'src/State.cpp', 'src/State.cpp',
'src/DAG.cpp', 'src/DAG.cpp',
'src/SymTable.cpp',
'src/Loader.cpp',
'src/Executor.cpp',
], ],
dependencies: [ dependencies: [
dependency('sqlite3') dependency('sqlite3')
@ -48,7 +45,6 @@ executable('snake-tests',
'tests/main.cpp', 'tests/main.cpp',
'tests/Lexer.cpp', 'tests/Lexer.cpp',
'tests/Parser.cpp', 'tests/Parser.cpp',
'tests/Interpreter.cpp',
], ],
dependencies: [ dependencies: [
snake_dep, snake_dep,

View File

@ -1,30 +0,0 @@
#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();
}
}

View File

@ -1,20 +0,0 @@
#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

View File

@ -5,12 +5,7 @@
namespace sn namespace sn
{ {
/*explicit*/ Interpreter::Interpreter(std::shared_ptr<State> state, /*explicit*/ Interpreter::Interpreter()
std::shared_ptr<Loader> loader,
std::shared_ptr<Executor> executor)
: m_state { state }
, m_loader { loader }
, m_executor { executor }
{ {
} }
@ -22,7 +17,7 @@ namespace sn
{ {
// Process the document ast // Process the document ast
// ------------------------ // ------------------------
auto node = m_loader->parse_snakefile(); auto node = parse_snakefile();
process(node); process(node);
// Get all files // Get all files
@ -52,9 +47,11 @@ namespace sn
// Get modified files // Get modified files
// ------------------ // ------------------
m_state->init(files); auto state = std::make_shared<State>();
auto modified_files = m_state->get_modified_files(files); state->init(files);
auto modified_files = state->get_modified_files(files);
// Build DAG // Build DAG
// --------- // ---------
@ -123,7 +120,7 @@ namespace sn
} }
} }
m_state->update(file); state->update(file);
} }
if (sorted.empty()) if (sorted.empty())
@ -132,21 +129,36 @@ namespace sn
} }
} }
std::string Interpreter::get_value(std::shared_ptr<Node> node) std::filesystem::path Interpreter::find_snakefile()
{ {
if (node->type() == NODE_IDENT) auto path = std::filesystem::current_path() / "Snakefile";
if (!std::filesystem::exists(path))
{ {
return node->repr(); throw run_error {"Snakefile not found"};
}
else if (node->type() == NODE_VAR)
{
auto var = *m_sym.get(node->repr());
return var.value;
} }
throw run_error {"cannot find value for '" return path;
+ SN_GET(NodeTypeStr, node->type(), "NODE_") }
+ "'"};
std::string Interpreter::load_snakefile()
{
std::ifstream file {find_snakefile()};
assert(file);
std::stringstream ss;
ss << file.rdbuf();
return ss.str();
}
std::shared_ptr<Node> Interpreter::parse_snakefile()
{
Lexer lexer;
lexer.scan(load_snakefile());
Parser parser;
auto node = parser.parse(lexer.all());
return node;
} }
std::vector<std::filesystem::path> std::vector<std::filesystem::path>
@ -179,12 +191,6 @@ namespace sn
} }
} break; } break;
case NODE_VAR_DECL: {
std::string name = (*node->get(0))->repr();
auto n = *node->get(1);
m_sym.declare(name, get_value(n));
} break;
case NODE_RULE: { case NODE_RULE: {
auto all_targets = *node->get(0); auto all_targets = *node->get(0);
auto deps = *node->get(1); auto deps = *node->get(1);
@ -197,11 +203,11 @@ namespace sn
for (size_t j=0; j<deps->size(); j++) for (size_t j=0; j<deps->size(); j++)
{ {
auto dep = *deps->get(j); auto dep = *deps->get(j);
auto path = format_path(get_value(dep)); auto path = format_path(dep->repr());
m_dependencies[format_path(get_value(target))].push_back(path); m_dependencies[format_path(target->repr())].push_back(path);
auto target_path = format_path(get_value(target)); auto target_path = format_path(target->repr());
m_scripts[target_path] = scripts(block); m_scripts[target_path] = scripts(block);
} }
@ -231,18 +237,7 @@ namespace sn
for (size_t j=0; j<cmd->size(); j++) for (size_t j=0; j<cmd->size(); j++)
{ {
auto c = *cmd->get(j); script += sep + (*cmd->get(j))->repr();
if (c->type() == NODE_IDENT)
{
script += sep + c->repr();
}
else
{
auto var = *m_sym.get(c->repr());
script += sep + var.value;
}
sep = " "; sep = " ";
} }
@ -254,7 +249,20 @@ namespace sn
std::string Interpreter::execute(std::string const& script) const std::string Interpreter::execute(std::string const& script) const
{ {
return m_executor->execute(script); 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();
} }
std::filesystem::path std::filesystem::path

View File

@ -5,9 +5,6 @@
#include "Node.hpp" #include "Node.hpp"
#include "State.hpp" #include "State.hpp"
#include "DAG.hpp" #include "DAG.hpp"
#include "SymTable.hpp"
#include "Loader.hpp"
#include "Executor.hpp"
namespace sn namespace sn
{ {
@ -21,26 +18,21 @@ namespace sn
class Interpreter class Interpreter
{ {
public: public:
explicit Interpreter(std::shared_ptr<State> state, explicit Interpreter();
std::shared_ptr<Loader> loader,
std::shared_ptr<Executor> executor);
virtual ~Interpreter(); virtual ~Interpreter();
void run(); void run();
private: std::filesystem::path find_snakefile();
std::shared_ptr<State> m_state; std::string load_snakefile();
std::shared_ptr<Loader> m_loader; std::shared_ptr<Node> parse_snakefile();
std::shared_ptr<Executor> m_executor;
private:
std::unordered_map<std::filesystem::path, std::unordered_map<std::filesystem::path,
std::vector<std::filesystem::path>> m_dependencies; std::vector<std::filesystem::path>> m_dependencies;
std::unordered_map<std::filesystem::path, std::unordered_map<std::filesystem::path,
std::vector<std::string>> m_scripts; std::vector<std::string>> m_scripts;
SymTable m_sym;
std::string get_value(std::shared_ptr<Node> node);
std::vector<std::filesystem::path> std::vector<std::filesystem::path>
targets(std::filesystem::path path) const; targets(std::filesystem::path path) const;
@ -53,6 +45,7 @@ namespace sn
std::filesystem::path std::filesystem::path
format_path(std::filesystem::path path) const; format_path(std::filesystem::path path) const;
}; };
} }

View File

@ -9,13 +9,11 @@ namespace sn
m_separators.push_back('\n'); m_separators.push_back('\n');
m_separators.push_back(' '); m_separators.push_back(' ');
add_text("=", NODE_ASSIGN);
add_text("->", NODE_RARROW); add_text("->", NODE_RARROW);
add_text("{", NODE_OBRACE); add_text("{", NODE_OBRACE);
add_text("}", NODE_CBRACE); add_text("}", NODE_CBRACE);
add_text(",", NODE_COMMA); add_text(",", NODE_COMMA);
m_scanners.push_back(std::bind(&Lexer::scan_var, this));
m_scanners.push_back(std::bind(&Lexer::scan_ident, this)); m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
} }
@ -153,39 +151,4 @@ namespace sn
return std::nullopt; return std::nullopt;
} }
std::optional<ScanInfo> Lexer::scan_var()
{
size_t cursor = m_cursor;
std::string repr;
if (cursor < m_source.size()
&& m_source[cursor] == '$')
{
repr += '$';
cursor++;
}
else
{
return std::nullopt;
}
while (cursor < m_source.size()
&& !is_separator(m_source[cursor]))
{
repr += m_source[cursor];
cursor++;
}
if (repr.empty() == false)
{
return ScanInfo {
cursor,
NODE_VAR,
repr.substr(1)
};
}
return std::nullopt;
}
} }

View File

@ -56,7 +56,6 @@ namespace sn
bool has_repr=false); bool has_repr=false);
std::optional<ScanInfo> scan_ident(); std::optional<ScanInfo> scan_ident();
std::optional<ScanInfo> scan_var();
}; };
} }

View File

@ -1,46 +0,0 @@
#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<Node> Loader::parse_snakefile()
{
Lexer lexer;
lexer.scan(load_snakefile());
Parser parser;
auto node = parser.parse(lexer.all());
return node;
}
}

View File

@ -1,30 +0,0 @@
#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<Node> parse_snakefile();
private:
};
}
#endif

View File

@ -6,8 +6,7 @@
#define NODE_TYPES(G) \ #define NODE_TYPES(G) \
G(NODE_DOC), G(NODE_IDENT), G(NODE_RARROW), G(NODE_OBRACE), \ G(NODE_DOC), G(NODE_IDENT), G(NODE_RARROW), G(NODE_OBRACE), \
G(NODE_CBRACE), G(NODE_CMD), G(NODE_CMD_LST), G(NODE_COMMA), \ G(NODE_CBRACE), G(NODE_CMD), G(NODE_CMD_LST), G(NODE_COMMA), \
G(NODE_BLOCK), G(NODE_TARGET), G(NODE_DEPS), G(NODE_RULE), \ G(NODE_BLOCK), G(NODE_TARGET), G(NODE_DEPS), G(NODE_RULE)
G(NODE_VAR), G(NODE_ASSIGN), G(NODE_VAR_DECL)
namespace sn namespace sn
{ {

View File

@ -65,19 +65,10 @@ namespace sn
auto node = std::make_shared<Node>(NODE_DOC); auto node = std::make_shared<Node>(NODE_DOC);
while (m_cursor < m_tokens.size()) while (m_cursor < m_tokens.size())
{
if (type_is(NODE_VAR)
&& type_is(NODE_ASSIGN, 1))
{
node->add_child(parse_var_decl());
}
else
{ {
node->add_child(parse_rule()); node->add_child(parse_rule());
} }
}
return node; return node;
} }
@ -100,11 +91,11 @@ namespace sn
{ {
auto node = std::make_shared<Node>(NODE_TARGET); auto node = std::make_shared<Node>(NODE_TARGET);
node->add_child(parse_literal()); node->add_child(*consume(NODE_IDENT));
while (!type_is(NODE_RARROW)) while (!type_is(NODE_RARROW))
{ {
node->add_child(parse_literal()); node->add_child(*consume(NODE_IDENT));
} }
consume(); consume();
@ -118,7 +109,7 @@ namespace sn
while (!type_is(NODE_OBRACE)) while (!type_is(NODE_OBRACE))
{ {
node->add_child(parse_literal()); node->add_child(*consume(NODE_IDENT));
} }
return node; return node;
@ -172,33 +163,8 @@ namespace sn
while (!type_is(NODE_CBRACE) && !type_is(NODE_COMMA)) while (!type_is(NODE_CBRACE) && !type_is(NODE_COMMA))
{ {
node->add_child(parse_literal());
}
return node;
}
std::shared_ptr<Node> Parser::parse_literal()
{
if (type_is(NODE_IDENT)
|| type_is(NODE_VAR))
{
return *consume();
}
throw syntax_error {"unknown token '"
+ SN_GET(NodeTypeStr,
m_tokens[m_cursor]->type(),
"NODE_")
+ "'"};
}
std::shared_ptr<Node> Parser::parse_var_decl()
{
auto node = std::make_shared<Node>(NODE_VAR_DECL);
node->add_child(*consume(NODE_VAR));
consume(NODE_ASSIGN);
node->add_child(*consume(NODE_IDENT)); node->add_child(*consume(NODE_IDENT));
}
return node; return node;
} }

View File

@ -37,9 +37,6 @@ namespace sn
std::shared_ptr<Node> parse_block(); std::shared_ptr<Node> parse_block();
std::shared_ptr<Node> parse_cmd_lst(); std::shared_ptr<Node> parse_cmd_lst();
std::shared_ptr<Node> parse_cmd(); std::shared_ptr<Node> parse_cmd();
std::shared_ptr<Node> parse_literal();
std::shared_ptr<Node> parse_var_decl();
}; };
} }

View File

@ -22,10 +22,10 @@ namespace sn
explicit State(); explicit State();
virtual ~State(); virtual ~State();
virtual void init(std::vector<std::filesystem::path> const& files); void init(std::vector<std::filesystem::path> const& files);
virtual void update(std::filesystem::path path); void update(std::filesystem::path path);
virtual std::vector<std::filesystem::path> std::vector<std::filesystem::path>
get_modified_files(std::vector<std::filesystem::path> const& files); get_modified_files(std::vector<std::filesystem::path> const& files);
std::filesystem::path format_path(std::filesystem::path path) const; std::filesystem::path format_path(std::filesystem::path path) const;

View File

@ -1,29 +0,0 @@
#include "SymTable.hpp"
namespace sn
{
/*explicit*/ SymTable::SymTable()
{
}
/*virtual*/ SymTable::~SymTable()
{
}
void SymTable::declare(std::string const& name, std::string const& value)
{
m_vars[name] = Var { value };
}
std::optional<Var> SymTable::get(std::string const& name) const
{
if (auto var=m_vars.find(name);
var != std::end(m_vars))
{
return var->second;
}
return std::nullopt;
}
}

View File

@ -1,34 +0,0 @@
#ifndef sn_SYMTABLE_HPP
#define sn_SYMTABLE_HPP
#include "commons.hpp"
#include "Node.hpp"
namespace sn
{
/**
* A variable of the snakefile.
**/
struct Var {
std::string value;
};
/**
* A repository to declare and get variables.
* @see Var
**/
class SymTable
{
public:
explicit SymTable();
virtual ~SymTable();
void declare(std::string const& name, std::string const& value);
std::optional<Var> get(std::string const& name) const;
private:
std::unordered_map<std::string, Var> m_vars;
};
}
#endif

View File

@ -3,15 +3,10 @@
#include "snake.hpp" #include "snake.hpp"
#include "Interpreter.hpp" #include "Interpreter.hpp"
#include "State.hpp" #include "State.hpp"
#include "Executor.hpp"
int main(int, char**) int main(int, char**)
{ {
auto state = std::make_shared<sn::State>(); sn::Interpreter interpreter;
auto loader = std::make_shared<sn::Loader>();
auto executor = std::make_shared<sn::Executor>();
sn::Interpreter interpreter {state, loader, executor};
interpreter.run(); interpreter.run();
return 0; return 0;

View File

@ -1,103 +0,0 @@
#include <catch2/catch.hpp>
#include "../src/Interpreter.hpp"
#define TEST_RUN(SRC, ...) \
test_run(SRC, {__VA_ARGS__})
using namespace sn;
struct StateMock: public State {
std::vector<std::filesystem::path> modified_files;
StateMock(std::vector<std::filesystem::path> const& p_modified_files)
: modified_files { p_modified_files }
{
}
virtual void init(std::vector<std::filesystem::path> const&) override
{
}
virtual void update(std::filesystem::path) override
{
}
virtual std::vector<std::filesystem::path>
get_modified_files(std::vector<std::filesystem::path> 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<std::string> history;
virtual std::string execute(std::string const& command) override
{
history.push_back(command);
return "";
}
};
class InterpreterTest
{
public:
explicit InterpreterTest() {}
virtual ~InterpreterTest() {}
std::vector<std::string>
test_run(std::string const& snakefile,
std::vector<std::filesystem::path> const& modified)
{
auto state = std::make_shared<StateMock>(modified);
auto loader = std::make_shared<LoaderMock>(snakefile);
auto executor = std::make_shared<ExecutorMock>();
Interpreter interpreter {state, loader, executor};
interpreter.run();
return executor->history;
}
void test_contains(std::string const& str, std::vector<std::string> 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> {
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);
}

View File

@ -41,13 +41,3 @@ TEST_CASE_METHOD(LexerTest, "Lexer_rule")
test_next("CBRACE"); test_next("CBRACE");
test_end(); test_end();
} }
TEST_CASE_METHOD(LexerTest, "Lexer_vars")
{
m_lexer.scan("$hello $WORLD = ");
test_next("VAR[hello]");
test_next("VAR[WORLD]");
test_next("ASSIGN");
test_end();
}

View File

@ -46,31 +46,3 @@ TEST_CASE_METHOD(ParserTest, "Parser_rule")
")))", ")))",
ss.str()); ss.str());
} }
TEST_CASE_METHOD(ParserTest, "Parser_var_decl_and_use")
{
std::stringstream ss;
ss << "$BIM = hello.cpp" << std::endl;
ss << "$BAM = world.hpp" << std::endl;
ss << " $BIM -> hello.cpp {" << std::endl;
ss << " g++ $BIM -o hello.elf, " << std::endl;
ss << " ls " << std::endl;
ss << " }" << std::endl;
test_parse("DOC("
"VAR_DECL(VAR[BIM],IDENT[hello.cpp]),"
"VAR_DECL(VAR[BAM],IDENT[world.hpp]),"
"RULE("
"TARGET("
"VAR[BIM]"
"),DEPS("
"IDENT[hello.cpp]"
"),BLOCK("
"CMD("
"IDENT[g++],VAR[BIM],IDENT[-o],IDENT[hello.elf]"
"),CMD("
"IDENT[ls]"
")"
")))",
ss.str());
}