diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 78ec232..7b95608 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -5,7 +5,13 @@ DEPS ::= LITERAL* BLOCK ::= obrace CMD_LST cbrace CMD_LST ::= (CMD (comma CMD)* comma?)? CMD ::= LITERAL* -LITERAL ::= ident | var | ARRAY | INDEX +LITERAL ::= +| ident +| var +| generic +| ARRAY +| INDEX + VAR_DECL ::= var assign (LITERAL | ARRAY | INDEX) ARRAY ::= opar LITERAL* cpar diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp index 2c5d21b..4924467 100644 --- a/src/Interpreter.cpp +++ b/src/Interpreter.cpp @@ -129,6 +129,11 @@ namespace sn } } } + else + { + // TODO TRY GENERICS + std::cout << "-> " << file << std::endl; + } m_state->update(file); } @@ -407,12 +412,20 @@ namespace sn auto deps = *node->get(1); auto block = *node->get(2); + if (!target_node->collect(NODE_GENERIC).empty() + || !deps->collect(NODE_GENERIC).empty()) + // Generic rule here + { + break; + } + std::vector target_values; std::vector target_values_verbatim; for (size_t i=0; isize(); i++) { auto t = *target_node->get(i); + auto values = get_value(t); std::copy(std::begin(values), std::end(values), diff --git a/src/Interpreter.hpp b/src/Interpreter.hpp index 134d334..6d19310 100644 --- a/src/Interpreter.hpp +++ b/src/Interpreter.hpp @@ -14,6 +14,25 @@ namespace sn { SN_ERROR(run_error); + /** + * Represents a snakefile value. + * (e.g a file, an array of files ...) + **/ + struct Value { + Value(std::filesystem::path p_file) + : file { p_file } + { + } + + Value(std::vector p_array) + : array { p_array } + { + } + + std::optional file; + std::optional> array; + }; + /** * Process a snakefile AST and run build commands. * @see Node diff --git a/src/Lexer.cpp b/src/Lexer.cpp index a43d877..0c86df8 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -20,6 +20,7 @@ namespace sn add_text("}", NODE_CBRACE); add_text(",", NODE_COMMA); + m_scanners.push_back(std::bind(&Lexer::scan_generic, this)); m_scanners.push_back(std::bind(&Lexer::scan_var, this)); m_scanners.push_back(std::bind(&Lexer::scan_ident, this)); } @@ -193,4 +194,39 @@ namespace sn return std::nullopt; } + + std::optional Lexer::scan_generic() + { + 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_GENERIC, + repr.substr(1) + }; + } + + return std::nullopt; + } } diff --git a/src/Lexer.hpp b/src/Lexer.hpp index 0763bcd..0aa41aa 100644 --- a/src/Lexer.hpp +++ b/src/Lexer.hpp @@ -57,6 +57,7 @@ namespace sn std::optional scan_ident(); std::optional scan_var(); + std::optional scan_generic(); }; } diff --git a/src/Node.cpp b/src/Node.cpp index 62ea647..32695e0 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -53,4 +53,24 @@ namespace sn return ss.str(); } + + std::vector> Node::collect(NodeType type) + { + std::vector> results; + + if (m_type == type) + { + return {shared_from_this()}; + } + + for (auto child: m_children) + { + auto collected = child->collect(type); + + std::copy(std::begin(collected), std::end(collected), + std::back_inserter(results)); + } + + return results; + } } diff --git a/src/Node.hpp b/src/Node.hpp index 0e68063..d490340 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -2,6 +2,7 @@ #define sn_NODE_HPP #include "commons.hpp" +#include #define NODE_TYPES(G) \ G(NODE_DOC), G(NODE_IDENT), G(NODE_RARROW), G(NODE_OBRACE), \ @@ -9,7 +10,7 @@ G(NODE_BLOCK), G(NODE_TARGET), G(NODE_DEPS), G(NODE_RULE), \ G(NODE_VAR), G(NODE_ASSIGN), G(NODE_VAR_DECL), G(NODE_OPAR), \ G(NODE_CPAR), G(NODE_ARRAY), G(NODE_INDEX), G(NODE_OSQUARE), \ - G(NODE_CSQUARE) + G(NODE_CSQUARE), G(NODE_GENERIC) namespace sn { @@ -18,7 +19,7 @@ namespace sn /** * A node is an element of the snake AST. **/ - class Node + class Node: public std::enable_shared_from_this { public: explicit Node(NodeType type, std::string const& repr=""); @@ -33,6 +34,7 @@ namespace sn std::optional> get(size_t index); std::string string() const; + std::vector> collect(NodeType type); private: NodeType m_type; diff --git a/src/Parser.cpp b/src/Parser.cpp index 7d4b144..46d8b67 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -187,7 +187,8 @@ namespace sn } if (type_is(NODE_IDENT) - || type_is(NODE_VAR)) + || type_is(NODE_VAR) + || type_is(NODE_GENERIC)) { return *consume(); } diff --git a/tests/Interpreter.cpp b/tests/Interpreter.cpp index b746663..d470704 100644 --- a/tests/Interpreter.cpp +++ b/tests/Interpreter.cpp @@ -452,3 +452,39 @@ TEST_CASE_METHOD(InterpreterTest, "Interpreter_array_constructor") test_contains(result, res); } } + +TEST_CASE_METHOD(InterpreterTest, "Interpreter_generics", "[.]") +{ + SECTION("no rule to apply") + { + std::stringstream ss; + ss << "^.o -> ^.cpp {" << std::endl; + ss << "$IN $OUT" << std::endl; + ss << "}" << std::endl; + + auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); + REQUIRE(res.empty()); + } + + SECTION("call left") + { + std::stringstream ss; + ss << "^.o -> ^.cpp ^.el {" << std::endl; + ss << "$IN $OUT" << std::endl; + ss << "}" << std::endl; + + auto res = TEST_RUN(ss.str(), std::filesystem::path("a.cpp")); + test_contains("a.o a.cpp", res); + } + + SECTION("call right") + { + std::stringstream ss; + ss << "^.o -> ^.cpp ^.el {" << std::endl; + ss << "$IN $OUT" << std::endl; + ss << "}" << std::endl; + + auto res = TEST_RUN(ss.str(), std::filesystem::path("a.el")); + test_contains("a.o a.el", res); + } +} diff --git a/tests/Lexer.cpp b/tests/Lexer.cpp index 43aa6bd..f84af44 100644 --- a/tests/Lexer.cpp +++ b/tests/Lexer.cpp @@ -62,3 +62,14 @@ TEST_CASE_METHOD(LexerTest, "Lexer_arrays") test_next("CSQUARE"); test_end(); } + +TEST_CASE_METHOD(LexerTest, "Lexer_generic") +{ + m_lexer.scan(" ^.o ^.cpp ^.tar.gz "); + + test_next("GENERIC[.o]"); + test_next("GENERIC[.cpp]"); + test_next("GENERIC[.tar.gz]"); + + test_end(); +} diff --git a/tests/Parser.cpp b/tests/Parser.cpp index f0e3a10..744b8c4 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -216,3 +216,20 @@ TEST_CASE_METHOD(ParserTest, "Parser_replace_ext_index") ")))", ss.str()); } + +TEST_CASE_METHOD(ParserTest, "Parser_generic") +{ + std::stringstream ss; + ss << " ^.o -> ^.c {" << std::endl; + ss << " g++ $IN -o $OUT " << std::endl; + ss << " }" << std::endl; + + test_parse("DOC(" + "RULE(" + "TARGET(GENERIC[.o])" + ",DEPS(GENERIC[.c])" + ",BLOCK(" + "CMD(IDENT[g++],VAR[IN],IDENT[-o],VAR[OUT])" + ")))", + ss.str()); +}