add leaves
Change-Id: I962bb4e74f39ec0a838f984b8c13a420ba36d7ce
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dd82c6e..9e21d4f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -110,6 +110,7 @@
endmacro()
cli_test(cd)
cli_test(presence_containers)
+ cli_test(leaf_editing)
endif()
diff --git a/src/ast.cpp b/src/ast.cpp
index 0e40b9c..35bf1cb 100644
--- a/src/ast.cpp
+++ b/src/ast.cpp
@@ -18,6 +18,16 @@
return this->m_name == b.m_name;
}
+leaf_::leaf_(const std::string& name)
+ : m_name(name)
+{
+}
+
+bool leaf_::operator==(const leaf_& b) const
+{
+ return this->m_name == b.m_name;
+}
+
listElement_::listElement_(const std::string& listName, const std::map<std::string, std::string>& keys)
: m_name(listName)
, m_keys(keys)
@@ -36,6 +46,11 @@
return this->m_nodes == b.m_nodes;
}
+bool set_::operator==(const set_& b) const
+{
+ return this->m_path == b.m_path && this->m_data == b.m_data;
+}
+
bool cd_::operator==(const cd_& b) const
{
return this->m_path == b.m_path;
diff --git a/src/ast.hpp b/src/ast.hpp
index 417c3d7..9638684 100644
--- a/src/ast.hpp
+++ b/src/ast.hpp
@@ -54,6 +54,15 @@
std::string m_name;
};
+struct leaf_ {
+ leaf_() = default;
+ leaf_(const std::string& name);
+
+ bool operator==(const leaf_& b) const;
+
+ std::string m_name;
+};
+
struct list_ {
std::vector<std::string> m_keys;
@@ -72,7 +81,7 @@
struct path_ {
bool operator==(const path_& b) const;
- std::vector<boost::variant<container_, listElement_, nodeup_>> m_nodes;
+ std::vector<boost::variant<container_, listElement_, nodeup_, leaf_>> m_nodes;
};
@@ -91,7 +100,13 @@
path_ m_path;
};
-using command_ = boost::variant<cd_, create_, delete_>;
+struct set_ : x3::position_tagged {
+ bool operator==(const set_& b) const;
+ path_ m_path;
+ std::string m_data;
+};
+
+using command_ = boost::variant<cd_, create_, delete_, set_>;
BOOST_FUSION_ADAPT_STRUCT(container_, m_name)
BOOST_FUSION_ADAPT_STRUCT(listElement_, m_name, m_keys)
@@ -99,3 +114,4 @@
BOOST_FUSION_ADAPT_STRUCT(cd_, m_path)
BOOST_FUSION_ADAPT_STRUCT(create_, m_path)
BOOST_FUSION_ADAPT_STRUCT(delete_, m_path)
+BOOST_FUSION_ADAPT_STRUCT(set_, m_path, m_data)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 1feeeab..4b703aa 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -98,7 +98,6 @@
}
};
-
struct nodeup_class {
template <typename T, typename Iterator, typename Context>
void on_success(Iterator const&, Iterator const&, T&, Context const& context)
@@ -128,11 +127,43 @@
}
};
+struct leaf_class {
+ template <typename T, typename Iterator, typename Context>
+ void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
+ {
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ const auto& schema = parserContext.m_schema;
+
+ if (schema.isLeaf(parserContext.m_curPath, ast.m_name)) {
+ parserContext.m_curPath.m_nodes.push_back(ast);
+ } else {
+ _pass(context) = false;
+ }
+ }
+};
+
struct path_class {
template <typename T, typename Iterator, typename Context>
void on_success(Iterator const&, Iterator const&, T&, Context const&)
{
}
+
+ template <typename Iterator, typename Exception, typename Context>
+ x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
+ {
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
+ std::string message = "invalid path.";
+ if (parserContext.m_errorHandled) // someone already handled our error
+ return x3::error_handler_result::fail;
+
+ parserContext.m_errorHandled = true;
+ error_handler(x.where(), message);
+ return x3::error_handler_result::fail;
+ }
+};
+
+struct data_string_class {
};
struct cd_class {
@@ -198,6 +229,33 @@
struct delete_class : public presenceContainerPathHandler {
};
+struct set_class {
+ template <typename T, typename Iterator, typename Context>
+ void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
+ {
+ try {
+ auto leaf = boost::get<leaf_>(ast.m_path.m_nodes.back());
+ } catch (boost::bad_get&) {
+ _pass(context) = false;
+ return;
+ }
+ }
+
+ template <typename Iterator, typename Exception, typename Context>
+ x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
+ {
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
+ std::string message = "This isn't a path to leaf.";
+ if (parserContext.m_errorHandled) // someone already handled our error
+ return x3::error_handler_result::fail;
+
+ parserContext.m_errorHandled = true;
+ error_handler(x.where(), message);
+ return x3::error_handler_result::fail;
+ }
+};
+
struct command_class {
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 30f69f1..9e4ea35 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -19,8 +19,11 @@
x3::rule<listElement_class, listElement_> const listElement = "listElement";
x3::rule<nodeup_class, nodeup_> const nodeup = "nodeup";
x3::rule<container_class, container_> const container = "container";
+x3::rule<leaf_class, leaf_> const leaf = "leaf";
x3::rule<path_class, path_> const path = "path";
+x3::rule<data_string_class, std::string> const data_string = "data_string";
x3::rule<cd_class, cd_> const cd = "cd";
+x3::rule<set_class, set_> const set = "set";
x3::rule<create_class, create_> const create = "create";
x3::rule<delete_class, delete_> const delete_rule = "delete_rule";
x3::rule<command_class, command_> const command = "command";
@@ -50,20 +53,33 @@
auto const container_def =
identifier;
+auto const leaf_def =
+ identifier;
+
+// leaf cannot be in the middle of a path, however, I need the grammar's attribute to be a vector of variants
auto const path_def =
- (container | listElement | nodeup) % '/';
+ (container | listElement | nodeup | leaf) % '/';
+
+auto const data_string_def =
+ lexeme[+char_];
+
+auto const space_separator =
+ x3::omit[x3::no_skip[space]];
auto const cd_def =
- lit("cd") > x3::omit[x3::no_skip[space]] > path >> x3::eoi;
+ lit("cd") > space_separator > path >> x3::eoi;
auto const create_def =
- lit("create") > x3::omit[x3::no_skip[space]] > path >> x3::eoi;
+ lit("create") > space_separator > path >> x3::eoi;
auto const delete_rule_def =
- lit("delete") > x3::omit[x3::no_skip[space]] > path >> x3::eoi;
+ lit("delete") > space_separator > path >> x3::eoi;
+
+auto const set_def =
+ lit("set") > space_separator > path > space_separator > data_string >> x3::eoi;
auto const command_def =
- cd | create | delete_rule;
+ cd | create | delete_rule | set;
BOOST_SPIRIT_DEFINE(keyValue)
BOOST_SPIRIT_DEFINE(identifier)
@@ -72,7 +88,10 @@
BOOST_SPIRIT_DEFINE(listElement)
BOOST_SPIRIT_DEFINE(nodeup)
BOOST_SPIRIT_DEFINE(container)
+BOOST_SPIRIT_DEFINE(leaf)
BOOST_SPIRIT_DEFINE(path)
+BOOST_SPIRIT_DEFINE(data_string)
+BOOST_SPIRIT_DEFINE(set)
BOOST_SPIRIT_DEFINE(cd)
BOOST_SPIRIT_DEFINE(create)
BOOST_SPIRIT_DEFINE(delete_rule)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 5c132d1..44c0b2f 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -9,6 +9,11 @@
#include <iostream>
#include "interpreter.hpp"
+void Interpreter::operator()(const set_& set) const
+{
+ std::cout << "Setting " << boost::get<leaf_>(set.m_path.m_nodes.back()).m_name << " to " << set.m_data << std::endl;
+}
+
void Interpreter::operator()(const cd_& cd) const
{
m_parser.changeNode(cd.m_path);
diff --git a/src/interpreter.hpp b/src/interpreter.hpp
index a4a5cc0..64cc0e9 100644
--- a/src/interpreter.hpp
+++ b/src/interpreter.hpp
@@ -14,6 +14,7 @@
struct Interpreter : boost::static_visitor<void> {
Interpreter(Parser& parser, Schema&);
+ void operator()(const set_&) const;
void operator()(const cd_&) const;
void operator()(const create_&) const;
void operator()(const delete_&) const;
diff --git a/src/main.cpp b/src/main.cpp
index 4fb9d8d..df4c925 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -49,7 +49,9 @@
Schema schema;
schema.addContainer("", "a", yang::ContainerTraits::Presence);
schema.addContainer("", "b");
+ schema.addLeaf("", "leaf");
schema.addContainer("a", "a2");
+ schema.addLeaf("a", "leafa");
schema.addContainer("b", "b2", yang::ContainerTraits::Presence);
schema.addContainer("a/a2", "a3", yang::ContainerTraits::Presence);
schema.addContainer("b/b2", "b3");
diff --git a/src/schema.cpp b/src/schema.cpp
index fa72f81..9832c45 100644
--- a/src/schema.cpp
+++ b/src/schema.cpp
@@ -115,3 +115,17 @@
std::string locationString = pathToString(location);
return boost::get<yang::container>(children(locationString).at(name)).m_presence == yang::ContainerTraits::Presence;
}
+
+void Schema::addLeaf(const std::string& location, const std::string& name)
+{
+ m_nodes.at(location).emplace(name, yang::leaf{});
+}
+
+bool Schema::isLeaf(const path_& location, const std::string& name) const
+{
+ std::string locationString = pathToString(location);
+ if (!nodeExists(locationString, name))
+ return false;
+
+ return children(locationString).at(name).type() == typeid(yang::leaf);
+}
diff --git a/src/schema.hpp b/src/schema.hpp
index 9c7114b..487f820 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -25,10 +25,12 @@
struct list {
std::set<std::string> m_keys;
};
+struct leaf {
+};
}
-using NodeType = boost::variant<yang::container, yang::list>;
+using NodeType = boost::variant<yang::container, yang::list, yang::leaf>;
class InvalidNodeException : public std::invalid_argument {
@@ -52,6 +54,8 @@
bool isList(const path_& location, const std::string& name) const;
void addList(const std::string& location, const std::string& name, const std::set<std::string>& keys);
bool isPresenceContainer(const path_& location, const std::string& name) const;
+ void addLeaf(const std::string& location, const std::string& name);
+ bool isLeaf(const path_& location, const std::string& name) const;
private:
const std::unordered_map<std::string, NodeType>& children(const std::string& name) const;
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
new file mode 100644
index 0000000..e31a73f
--- /dev/null
+++ b/tests/leaf_editing.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
+ * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
+ *
+ * Written by Václav Kubernát <kubervac@fit.cvut.cz>
+ *
+*/
+
+#include "trompeloeil_catch.h"
+#include "ast.hpp"
+#include "parser.hpp"
+#include "schema.hpp"
+
+TEST_CASE("leaf editing")
+{
+ Schema schema;
+ schema.addContainer("", "contA");
+ schema.addLeaf("", "leaf");
+ schema.addLeaf("contA", "leafInCont");
+ schema.addList("", "list", {"number"});
+ schema.addLeaf("list", "leafInList");
+ Parser parser(schema);
+ std::string input;
+ std::ostringstream errorStream;
+
+ SECTION("valid input")
+ {
+ set_ expected;
+
+ SECTION("set leaf some_data")
+ {
+ input = "set leaf some_data";
+ expected.m_path.m_nodes.push_back(leaf_("leaf"));
+ expected.m_data = "some_data";
+ }
+
+ SECTION("set contA/leafInCont more_data")
+ {
+ input = "set contA/leafInCont more_data";
+ expected.m_path.m_nodes.push_back(container_("contA"));
+ expected.m_path.m_nodes.push_back(leaf_("leafInCont"));
+ expected.m_data = "more_data";
+ }
+
+ command_ command = parser.parseCommand(input, errorStream);
+ REQUIRE(command.type() == typeid(set_));
+ REQUIRE(boost::get<set_>(command) == expected);
+ }
+
+ SECTION("invalid input")
+ {
+ SECTION("missing space between a command and its arguments")
+ {
+ SECTION("setleaf some_data")
+ {
+ input = "setleaf some_data";
+ }
+ }
+
+ SECTION("missing space between arguments")
+ {
+ SECTION("set leaflol")
+ {
+ input = "set leaflol";
+ }
+ }
+
+ SECTION("non-leaf identifiers")
+ {
+ SECTION("set nonexistent blabla")
+ {
+ input = "set nonexistent blabla";
+ }
+
+ SECTION("set contA abde")
+ {
+ input = "set contA abde";
+ }
+ }
+
+ REQUIRE_THROWS(parser.parseCommand(input, errorStream));
+ }
+}