✨ can now build arrays using wildcards.
parent
61e4f761c1
commit
1ef177bfc8
|
@ -25,6 +25,7 @@ snake_lib = static_library('snake',
|
|||
'src/SymTable.cpp',
|
||||
'src/Loader.cpp',
|
||||
'src/Executor.cpp',
|
||||
'src/Finder.cpp',
|
||||
],
|
||||
dependencies: [
|
||||
dependency('sqlite3')
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
#include "Finder.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
namespace sn
|
||||
{
|
||||
/*explicit*/ Finder::Finder()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Finder::~Finder()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ std::vector<std::filesystem::path>
|
||||
Finder::get_paths(std::filesystem::path root)
|
||||
{
|
||||
std::vector<std::filesystem::path> paths;
|
||||
|
||||
for (auto entry: std::filesystem::directory_iterator(root))
|
||||
{
|
||||
if (std::filesystem::is_directory(entry.path()))
|
||||
{
|
||||
auto more = get_paths(entry.path());
|
||||
|
||||
std::copy(std::begin(more), std::end(more),
|
||||
std::back_inserter(paths));
|
||||
}
|
||||
|
||||
paths.push_back(entry.path());
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
/* virtual*/ bool Finder::match(std::string const& text,
|
||||
std::filesystem::path const& path)
|
||||
{
|
||||
if (text == "*")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (text.size() > 1 && text.substr(0, 2) == "*.")
|
||||
{
|
||||
auto ext = text.substr(1);
|
||||
return path.extension() == ext;
|
||||
}
|
||||
|
||||
if (auto wildcard_pos = text.find("*");
|
||||
wildcard_pos != std::string::npos)
|
||||
{
|
||||
auto dir = std::filesystem::path(text.substr(0, wildcard_pos));
|
||||
auto ext = text.substr(wildcard_pos + 1);
|
||||
|
||||
if (dir.empty() == false)
|
||||
{
|
||||
dir = dir.string().substr(0, dir.string().size() - 1);
|
||||
}
|
||||
|
||||
if (path.parent_path().is_absolute())
|
||||
{
|
||||
dir = std::filesystem::absolute(dir);
|
||||
}
|
||||
|
||||
return path.parent_path() == dir
|
||||
&& (ext.empty()
|
||||
|| path.has_extension() == false
|
||||
|| path.extension() == ext);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef sn_FINDER_HPP
|
||||
#define sn_FINDER_HPP
|
||||
|
||||
#include "commons.hpp"
|
||||
|
||||
namespace sn
|
||||
{
|
||||
/**
|
||||
* Find files from a wildcard expression.
|
||||
**/
|
||||
class Finder
|
||||
{
|
||||
public:
|
||||
explicit Finder();
|
||||
virtual ~Finder();
|
||||
|
||||
virtual std::vector<std::filesystem::path>
|
||||
get_paths(std::filesystem::path root);
|
||||
|
||||
virtual bool match(std::string const& text,
|
||||
std::filesystem::path const& path);
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -8,10 +8,12 @@ namespace sn
|
|||
/*explicit*/ Interpreter::Interpreter(std::shared_ptr<State> state,
|
||||
std::shared_ptr<Loader> loader,
|
||||
std::shared_ptr<Executor> executor,
|
||||
std::shared_ptr<Finder> finder,
|
||||
std::ostream& ostream)
|
||||
: m_state { state }
|
||||
, m_loader { loader }
|
||||
, m_executor { executor }
|
||||
, m_finder { finder }
|
||||
, m_ostream { ostream }
|
||||
{
|
||||
}
|
||||
|
@ -25,6 +27,9 @@ namespace sn
|
|||
// Process the document ast
|
||||
// ------------------------
|
||||
auto node = m_loader->parse_snakefile();
|
||||
|
||||
m_files = collect(node);
|
||||
|
||||
process(node);
|
||||
|
||||
// Get all files
|
||||
|
@ -52,10 +57,10 @@ namespace sn
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Get modified files
|
||||
// ------------------
|
||||
m_state->init(files);
|
||||
|
||||
auto modified_files = m_state->get_modified_files(files);
|
||||
|
||||
// Build DAG
|
||||
|
@ -159,7 +164,34 @@ namespace sn
|
|||
|
||||
for (size_t i=0; i<node->size(); i++)
|
||||
{
|
||||
auto vals = get_value(*node->get(i));
|
||||
std::string text = (*node->get(i))->repr();
|
||||
std::vector<std::string> vals;
|
||||
|
||||
if (text.find('*') != std::string::npos)
|
||||
// wildcard array constructor
|
||||
{
|
||||
auto candidate =
|
||||
m_finder->get_paths(m_loader->find_snakefile().parent_path());
|
||||
|
||||
std::copy(std::begin(m_files), std::end(m_files),
|
||||
std::back_inserter(candidate));
|
||||
|
||||
std::sort(std::begin(candidate), std::end(candidate));
|
||||
candidate.erase(std::unique(std::begin(candidate),
|
||||
std::end(candidate)),
|
||||
std::end(candidate));
|
||||
|
||||
std::copy_if(std::begin(candidate),
|
||||
std::end(candidate),
|
||||
std::back_inserter(vals),
|
||||
[&](auto f){
|
||||
return m_finder->match(text, f);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
vals = get_value(*node->get(i));
|
||||
}
|
||||
|
||||
for (size_t j=0; j<vals.size(); j++)
|
||||
{
|
||||
|
@ -261,6 +293,96 @@ namespace sn
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path>
|
||||
Interpreter::collect(std::shared_ptr<Node> node) const
|
||||
{
|
||||
std::vector<std::filesystem::path> results;
|
||||
|
||||
switch (node->type())
|
||||
{
|
||||
case NODE_RULE: {
|
||||
auto target_node = *node->get(0);
|
||||
auto deps = *node->get(1);
|
||||
auto block = *node->get(2);
|
||||
|
||||
std::vector<std::string> target_values;
|
||||
std::vector<std::string> target_values_verbatim;
|
||||
|
||||
for (size_t i=0; i<target_node->size(); i++)
|
||||
{
|
||||
auto t = *target_node->get(i);
|
||||
|
||||
try
|
||||
{
|
||||
auto values = get_value(t);
|
||||
|
||||
std::copy(std::begin(values), std::end(values),
|
||||
std::back_inserter(target_values_verbatim));
|
||||
|
||||
std::transform(std::begin(values),
|
||||
std::end(values),
|
||||
std::back_inserter(target_values),
|
||||
std::bind(&Interpreter::format_path, this,
|
||||
std::placeholders::_1));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> dep_values;
|
||||
std::vector<std::string> dep_values_verbatim;
|
||||
|
||||
for (size_t i=0; i<deps->size(); i++)
|
||||
{
|
||||
auto d = *deps->get(i);
|
||||
|
||||
try
|
||||
{
|
||||
auto values = get_value(d);
|
||||
|
||||
std::copy(std::begin(values), std::end(values),
|
||||
std::back_inserter(dep_values_verbatim));
|
||||
|
||||
std::transform(std::begin(values), std::end(values),
|
||||
std::back_inserter(dep_values),
|
||||
std::bind(&Interpreter::format_path, this,
|
||||
std::placeholders::_1));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
std::copy(std::begin(target_values_verbatim),
|
||||
std::end(target_values_verbatim),
|
||||
std::back_inserter(results));
|
||||
|
||||
std::copy(std::begin(dep_values_verbatim),
|
||||
std::end(dep_values_verbatim),
|
||||
std::back_inserter(results));
|
||||
} break;
|
||||
|
||||
default: {
|
||||
for (size_t i=0; i<node->size(); i++)
|
||||
{
|
||||
auto r = collect(*node->get(i));
|
||||
std::copy(std::begin(r), std::end(r),
|
||||
std::back_inserter(results));
|
||||
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
std::sort(std::begin(results), std::end(results));
|
||||
|
||||
results.erase(std::unique(std::begin(results),
|
||||
std::end(results)),
|
||||
std::end(results));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
void Interpreter::process(std::shared_ptr<Node> node)
|
||||
{
|
||||
switch (node->type())
|
||||
|
@ -276,9 +398,8 @@ namespace sn
|
|||
std::string name = (*node->get(0))->repr();
|
||||
|
||||
auto val = *node->get(1);
|
||||
|
||||
m_sym.declare(name, get_value(val));
|
||||
|
||||
auto values = get_value(val);
|
||||
m_sym.declare(name, values);
|
||||
} break;
|
||||
|
||||
case NODE_RULE: {
|
||||
|
@ -363,6 +484,11 @@ namespace sn
|
|||
|
||||
for (auto val: values)
|
||||
{
|
||||
if (std::filesystem::is_regular_file(val))
|
||||
{
|
||||
val = std::filesystem::relative(val, m_loader->find_snakefile().parent_path()).string();
|
||||
}
|
||||
|
||||
script += vsep + val;
|
||||
vsep = " ";
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "SymTable.hpp"
|
||||
#include "Loader.hpp"
|
||||
#include "Executor.hpp"
|
||||
#include "Finder.hpp"
|
||||
|
||||
namespace sn
|
||||
{
|
||||
|
@ -24,6 +25,7 @@ namespace sn
|
|||
explicit Interpreter(std::shared_ptr<State> state,
|
||||
std::shared_ptr<Loader> loader,
|
||||
std::shared_ptr<Executor> executor,
|
||||
std::shared_ptr<Finder> finder,
|
||||
std::ostream& ostream);
|
||||
virtual ~Interpreter();
|
||||
|
||||
|
@ -33,7 +35,10 @@ namespace sn
|
|||
std::shared_ptr<State> m_state;
|
||||
std::shared_ptr<Loader> m_loader;
|
||||
std::shared_ptr<Executor> m_executor;
|
||||
std::shared_ptr<Finder> m_finder;
|
||||
std::ostream& m_ostream;
|
||||
std::vector<std::filesystem::path> m_files;
|
||||
|
||||
std::unordered_map<std::filesystem::path,
|
||||
std::vector<std::filesystem::path>> m_dependencies;
|
||||
|
||||
|
@ -46,6 +51,9 @@ namespace sn
|
|||
std::vector<std::filesystem::path>
|
||||
targets(std::filesystem::path path) const;
|
||||
|
||||
std::vector<std::filesystem::path>
|
||||
collect(std::shared_ptr<Node> node) const;
|
||||
|
||||
void process(std::shared_ptr<Node> node);
|
||||
|
||||
std::vector<std::string> scripts(std::shared_ptr<Node> block) const;
|
||||
|
|
|
@ -4,14 +4,16 @@
|
|||
#include "Interpreter.hpp"
|
||||
#include "State.hpp"
|
||||
#include "Executor.hpp"
|
||||
#include "Finder.hpp"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
auto state = std::make_shared<sn::State>();
|
||||
auto loader = std::make_shared<sn::Loader>();
|
||||
auto executor = std::make_shared<sn::Executor>();
|
||||
auto finder = std::make_shared<sn::Finder>();
|
||||
|
||||
sn::Interpreter interpreter {state, loader, executor, std::cout};
|
||||
sn::Interpreter interpreter {state, loader, executor, finder, std::cout};
|
||||
interpreter.run();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -55,6 +55,21 @@ struct ExecutorMock: public Executor {
|
|||
}
|
||||
};
|
||||
|
||||
struct FinderMock: public Finder {
|
||||
std::vector<std::filesystem::path> files;
|
||||
|
||||
explicit FinderMock(std::vector<std::filesystem::path> const& p_files)
|
||||
: files { p_files }
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::vector<std::filesystem::path>
|
||||
get_paths(std::filesystem::path) override
|
||||
{
|
||||
return files;
|
||||
}
|
||||
};
|
||||
|
||||
class InterpreterTest
|
||||
{
|
||||
public:
|
||||
|
@ -69,19 +84,63 @@ public:
|
|||
|
||||
auto loader = std::make_shared<LoaderMock>(snakefile);
|
||||
auto executor = std::make_shared<ExecutorMock>();
|
||||
auto finder = std::make_shared<FinderMock>
|
||||
(std::vector<std::filesystem::path>{
|
||||
"first.cpp",
|
||||
"src/second.py",
|
||||
"third.py",
|
||||
"src/fourth.hpp",
|
||||
"fifth",
|
||||
"src/build/target/sixth"
|
||||
});
|
||||
|
||||
std::stringstream ss;
|
||||
Interpreter interpreter {state, loader, executor, ss};
|
||||
Interpreter interpreter {state, loader, executor, finder, ss};
|
||||
interpreter.run();
|
||||
|
||||
return executor->history;
|
||||
}
|
||||
|
||||
std::string sort_line(std::string const& line)
|
||||
{
|
||||
std::vector<std::string> words;
|
||||
std::string word;
|
||||
std::stringstream ss;
|
||||
ss << line;
|
||||
|
||||
while (std::getline(ss, word, ' '))
|
||||
{
|
||||
words.push_back(word);
|
||||
}
|
||||
|
||||
std::sort(std::begin(words), std::end(words));
|
||||
|
||||
std::string res = "";
|
||||
|
||||
std::string sep = "";
|
||||
|
||||
for (auto w: words)
|
||||
{
|
||||
res += sep + w;
|
||||
sep = " ";
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void test_contains(std::string const& str, std::vector<std::string> vec)
|
||||
{
|
||||
INFO("'" << str << "' not found, got '" << vec[0] << "'");
|
||||
REQUIRE(std::find(std::begin(vec), std::end(vec), str)
|
||||
!= std::end(vec));
|
||||
if (vec.empty())
|
||||
{
|
||||
INFO("'" << str << "' not found, got nothing");
|
||||
REQUIRE(false);
|
||||
return;
|
||||
}
|
||||
|
||||
INFO(str << "not found");
|
||||
REQUIRE(std::any_of(std::begin(vec), std::end(vec), [&](auto const& line){
|
||||
return sort_line(line) == sort_line(str);
|
||||
}));
|
||||
}
|
||||
|
||||
void test_not_contains(std::string const& str, std::vector<std::string> vec)
|
||||
|
@ -333,3 +392,63 @@ TEST_CASE_METHOD(InterpreterTest, "Interpreter_in_out")
|
|||
test_contains("a b c x y z", res);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(InterpreterTest, "Interpreter_array_constructor")
|
||||
{
|
||||
SECTION("extension I")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "x y z -> (*.py) {" << std::endl;
|
||||
ss << "$IN" << std::endl;
|
||||
ss << "}" << std::endl;
|
||||
|
||||
std::string result;
|
||||
result = "src/second.py third.py";
|
||||
|
||||
auto res = TEST_RUN(ss.str(), std::filesystem::path("third.py"));
|
||||
test_contains(result, res);
|
||||
}
|
||||
|
||||
SECTION("extension II")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "x y z -> (*.cpp) {" << std::endl;
|
||||
ss << "$IN" << std::endl;
|
||||
ss << "}" << std::endl;
|
||||
|
||||
std::string result;
|
||||
result = "first.cpp";
|
||||
|
||||
auto res = TEST_RUN(ss.str(), std::filesystem::path("first.cpp"));
|
||||
test_contains(result, res);
|
||||
}
|
||||
|
||||
SECTION("all in directory")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "x y z -> (src/*) {" << std::endl;
|
||||
ss << "$IN" << std::endl;
|
||||
ss << "}" << std::endl;
|
||||
|
||||
std::string result;
|
||||
result = "src/second.py src/fourth.hpp";
|
||||
|
||||
auto res = TEST_RUN(ss.str(), std::filesystem::path("src/second.py"));
|
||||
test_contains(result, res);
|
||||
}
|
||||
|
||||
SECTION("all in sub directories")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "x y z -> (src/build/target/*) {" << std::endl;
|
||||
ss << "$IN" << std::endl;
|
||||
ss << "}" << std::endl;
|
||||
|
||||
std::string result;
|
||||
result = "src/build/target/sixth";
|
||||
|
||||
auto res = TEST_RUN(ss.str(),
|
||||
std::filesystem::path("src/build/target/sixth"));
|
||||
test_contains(result, res);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue