add test for basic cd parsing
Change-Id: If35d62e323d48db11dc4128fb5c2898ef4ef63a6
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad8c3c2..3ac9278 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,15 +47,21 @@
find_package(spdlog REQUIRED)
find_package(Boost REQUIRED)
-set(netconf-cli_SRCS
- src/main.cpp
+set(parser_SRCS
src/CTree.cpp
src/CParser.cpp
src/ast.cpp
)
+add_library(parser STATIC ${parser_SRCS})
+target_link_libraries(parser Boost::boost)
+
+set(netconf-cli_SRCS
+ src/main.cpp
+ )
+
add_executable(netconf-cli ${netconf-cli_SRCS})
-target_link_libraries(netconf-cli docopt Boost::boost)
+target_link_libraries(netconf-cli docopt parser)
add_dependencies(netconf-cli target-NETCONF_CLI_VERSION)
target_include_directories(netconf-cli PRIVATE ${PROJECT_BINARY_DIR})
@@ -85,7 +91,7 @@
macro(cli_test fname)
set(test_${fname}_SOURCES tests/${fname}.cpp)
add_executable(test_${fname} ${test_${fname}_SOURCES})
- target_link_libraries(test_${fname} TestCatchIntegration)
+ target_link_libraries(test_${fname} TestCatchIntegration parser)
if(NOT CMAKE_CROSSCOMPILING)
add_test(test_${fname} test_${fname})
endif()
@@ -93,6 +99,8 @@
target_link_libraries(test_${fname} TestCatchIntegration)
endmacro()
cli_test(dummy)
+ cli_test(cd)
+
endif()
if(WITH_DOCS)
diff --git a/src/CParser.cpp b/src/CParser.cpp
index 75af7c0..618a721 100644
--- a/src/CParser.cpp
+++ b/src/CParser.cpp
@@ -1,3 +1,10 @@
+/*
+ * 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 "CParser.hpp"
TooManyArgumentsException::~TooManyArgumentsException() = default;
@@ -8,16 +15,19 @@
}
-Cmd CParser::parseInput(const std::string& line)
+cd_ CParser::parseCommand(const std::string& line)
{
- Cmd args;
+ cd_ parsedCommand;
+ ParserContext ctx(m_tree);
auto it = line.begin();
- //bool result = x3::phrase_parse(it, line.end(), command, space, args);
- //std::cout << "success: " << result << std::endl;
+
+ auto grammar = x3::with<parser_context_tag>(ctx)[cd];
+
+ x3::phrase_parse(it, line.end(), grammar, space, parsedCommand);
if (it != line.end()) {
throw TooManyArgumentsException(std::string(it, line.end()));
}
- return args;
+ return parsedCommand;
}
diff --git a/src/CParser.hpp b/src/CParser.hpp
index c3333af..96d4979 100644
--- a/src/CParser.hpp
+++ b/src/CParser.hpp
@@ -1,13 +1,15 @@
/*
* 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>
*
- */
+*/
#pragma once
#include <boost/spirit/home/x3.hpp>
#include "CTree.hpp"
+#include "ast.hpp"
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using Cmd = std::vector<std::string>;
@@ -27,7 +29,7 @@
class CParser {
public:
CParser(const CTree& tree);
- Cmd parseInput(const std::string& line);
+ cd_ parseCommand(const std::string& line);
private:
const CTree& m_tree;
diff --git a/src/CTree.cpp b/src/CTree.cpp
index 994515a..7d89cf3 100644
--- a/src/CTree.cpp
+++ b/src/CTree.cpp
@@ -1,5 +1,6 @@
/*
* 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>
*
@@ -17,21 +18,47 @@
return prefix + '/' + suffix;
}
-const std::unordered_set<std::string>& CTree::children(const std::string& node) const
+bool TreeNode::operator<(const TreeNode& b) const
+{
+ return this->m_name < b.m_name;
+}
+
+CTree::CTree()
+{
+ m_nodes.emplace("", std::unordered_map<std::string, NODE_TYPE>());
+}
+
+const std::unordered_map<std::string, NODE_TYPE>& CTree::children(const std::string& node) const
{
return m_nodes.at(node);
}
-bool CTree::checkNode(const std::string& location, const std::string& node) const
+bool CTree::nodeExists(const std::string& location, const std::string& node) const
{
- if (node == ".." || node.empty())
+ if (node.empty())
return true;
- const auto& childrenRef = children(location); //first, get a reference to all children
- if (childrenRef.find(node) == childrenRef.end()) { //find the desired node, if it isn't present throw an exception
- throw InvalidNodeException(node);
- }
- return true;
+ const auto& childrenRef = children(location);
+
+ return childrenRef.find(node) != childrenRef.end();
}
+
+bool CTree::isContainer(const std::string& location, const std::string& node) const
+{
+ if (!nodeExists(location, node))
+ return false;
+ return children(location).at(node) == TYPE_CONTAINER;
+}
+
+void CTree::addContainer(const std::string& location, const std::string& name)
+{
+ m_nodes.at(location).emplace(name, TYPE_CONTAINER);
+
+ //create a new set of children for the new node
+ std::string key = joinPaths(location, name);
+ m_nodes.emplace(key, std::unordered_map<std::string, NODE_TYPE>());
+}
+
+
void CTree::changeNode(const std::string& node)
{
if (node.empty()) {
@@ -44,21 +71,3 @@
{
return m_curDir;
}
-
-void CTree::addNode(const std::string& location, const std::string& name)
-{
- m_nodes.at(location).insert(name);
-
- //create a new set of children for the new node
- m_nodes.emplace(joinPaths(location, name), std::unordered_set<std::string>());
-}
-void CTree::initDefault()
-{
- m_nodes.emplace("", std::unordered_set<std::string>());
- addNode("", "aaa");
- addNode("", "bbb");
- addNode("", "ccc");
- addNode("aaa", "aaabbb");
- addNode("aaa", "aaauuu");
- addNode("bbb", "bbbuuu");
-}
diff --git a/src/CTree.hpp b/src/CTree.hpp
index 88bccae..fa99df6 100644
--- a/src/CTree.hpp
+++ b/src/CTree.hpp
@@ -1,5 +1,6 @@
/*
* 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>
*
@@ -9,13 +10,23 @@
#include <stdexcept>
#include <unordered_map>
-#include <unordered_set>
+
+enum NODE_TYPE {
+ TYPE_CONTAINER,
+ TYPE_LIST,
+ TYPE_LIST_ELEMENT
+};
+
+struct TreeNode {
+ bool operator<(const TreeNode& b) const;
+ std::string m_name;
+ NODE_TYPE m_type;
+};
class InvalidNodeException : public std::invalid_argument {
public:
using std::invalid_argument::invalid_argument;
~InvalidNodeException() override;
-
};
/*! \class CTree
@@ -26,15 +37,17 @@
* */
class CTree {
public:
- bool checkNode(const std::string& location, const std::string& node) const;
+ CTree();
+ bool nodeExists(const std::string& location, const std::string& node) const;
+
+ bool isContainer(const std::string& location, const std::string& node) const;
+ void addContainer(const std::string& location, const std::string& node);
void changeNode(const std::string& node);
- void initDefault();
std::string currentNode() const;
private:
- void addNode(const std::string& location, const std::string& node);
- const std::unordered_set<std::string>& children(const std::string& node) const;
+ const std::unordered_map<std::string, NODE_TYPE>& children(const std::string& node) const;
- std::unordered_map<std::string, std::unordered_set<std::string>> m_nodes;
+ std::unordered_map<std::string, std::unordered_map<std::string, NODE_TYPE>> m_nodes;
std::string m_curDir;
};
diff --git a/src/ast.cpp b/src/ast.cpp
index d9964ea..762c7cc 100644
--- a/src/ast.cpp
+++ b/src/ast.cpp
@@ -1,35 +1,36 @@
/*
* 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 "ast.hpp"
-
-
-template <typename T, typename Iterator, typename Context>
-inline void container_class::on_success(Iterator const& first, Iterator const& last
- , T& ast, Context const& context)
+container_::container_(const std::string& name)
+ : m_name(name)
{
- ast.m_name = ast.m_first + ast.m_name;
- //std::cout <<"parsed " << ast.m_name << "(container)\n";
}
-template <typename T, typename Iterator, typename Context>
-inline void path_class::on_success(Iterator const& first, Iterator const& last
- , T& ast, Context const& context)
+bool container_::operator==(const container_& b) const
{
- //std::cout << "parsed path:" << std::endl;
- //for (auto it : ast.m_nodes)
- //std::cout << it.m_name << std::endl;
+ return this->m_name == b.m_name;
}
-template <typename T, typename Iterator, typename Context>
-inline void cd_class::on_success(Iterator const& first, Iterator const& last
- , T& ast, Context const& context)
+bool path_::operator==(const path_& b) const
{
- //std::cout << "parsed cd! final path:" << std::endl;
- //for (auto it : ast.m_path.m_nodes)
- //std::cout << it.m_name << std::endl;
+ if (this->m_nodes.size() != b.m_nodes.size())
+ return false;
+ return this->m_nodes == b.m_nodes;
+}
+bool cd_::operator==(const cd_& b) const
+{
+ return this->m_path == b.m_path;
+}
+
+
+ParserContext::ParserContext(const CTree& tree)
+ : m_tree(tree)
+{
+ m_curPath = m_tree.currentNode();
}
diff --git a/src/ast.hpp b/src/ast.hpp
index 6c60dc9..f55ab39 100644
--- a/src/ast.hpp
+++ b/src/ast.hpp
@@ -1,15 +1,18 @@
/*
* 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>
*
*/
#pragma once
#include <boost/spirit/home/x3.hpp>
-#include <vector>
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
+
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
+#include <vector>
+
#include "CTree.hpp"
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
@@ -22,76 +25,88 @@
using x3::lexeme;
using ascii::space;
-using nodeString = std::string;
-
-struct ParserContext
-{
+struct ParserContext {
ParserContext(const CTree& tree);
const CTree& m_tree;
- std::string m_currentContext;
+ std::string m_curPath;
};
struct parser_context_tag;
-struct container_
-{
- char m_first;
+struct container_ {
+ container_() {}
+ container_(const std::string& name);
+
+ bool operator==(const container_& b) const;
+
+ char m_first = ' ';
std::string m_name;
};
BOOST_FUSION_ADAPT_STRUCT(container_, m_first, m_name)
-struct path_
-{
+struct path_ {
+ bool operator==(const path_& b) const;
std::vector<container_> m_nodes;
};
BOOST_FUSION_ADAPT_STRUCT(path_, m_nodes)
-struct cd_
-{
+struct cd_ {
+ bool operator==(const cd_& b) const;
path_ m_path;
};
BOOST_FUSION_ADAPT_STRUCT(cd_, m_path)
-struct container_class
-{
+struct container_class {
template <typename T, typename Iterator, typename Context>
- inline void on_success(Iterator const& first, Iterator const& last
- , T& ast, Context const& context);
+ void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
+ {
+ ast.m_name = ast.m_first + ast.m_name;
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ const auto& tree = parserContext.m_tree;
+
+ if (tree.isContainer(parserContext.m_curPath, ast.m_name)) {
+ if (!parserContext.m_curPath.empty()) {
+ parserContext.m_curPath += '/';
+ }
+ parserContext.m_curPath += ast.m_name;
+ } else {
+ throw InvalidNodeException("No container with the name \"" + ast.m_name + "\" in \"" + parserContext.m_curPath + "\"");
+ }
+ }
};
-struct path_class
-{
+struct path_class {
template <typename T, typename Iterator, typename Context>
- inline void on_success(Iterator const& first, Iterator const& last
- , T& ast, Context const& context);
+ void on_success(Iterator const&, Iterator const&, T&, Context const&)
+ {
+ }
};
-struct cd_class
-{
+struct cd_class {
template <typename T, typename Iterator, typename Context>
- inline void on_success(Iterator const& first, Iterator const& last
- , T& ast, Context const& context);
-
+ void on_success(Iterator const&, Iterator const&, T&, Context const&)
+ {
+ }
};
-typedef x3::rule<container_class, container_> container_type;
-typedef x3::rule<path_class, path_> path_type;
-typedef x3::rule<cd_class, cd_> cd_type;
+x3::rule<container_class, container_> const container = "container";
+x3::rule<path_class, path_> const path = "path";
+x3::rule<cd_class, cd_> const cd = "cd";
-container_type const container = "container";
-path_type const path = "path";
-cd_type const cd = "cd";
+auto const identifier =
+ lexeme[
+ ((alpha | char_("_")) >> *(alnum | char_("_") | char_("-") | char_(".")))
+ ];
auto const container_def =
- lexeme[
- ((alpha | x3::string("_")) >> *(alnum | x3::string("_") | x3::string("-") | x3::string(".")))
- ];
+ identifier;
+
auto const path_def =
container % '/';
diff --git a/tests/cd.cpp b/tests/cd.cpp
new file mode 100644
index 0000000..39487a3
--- /dev/null
+++ b/tests/cd.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "CParser.hpp"
+#include "CTree.hpp"
+#include "ast.hpp"
+
+TEST_CASE("cd")
+{
+ CTree tree;
+ tree.addContainer("", "a");
+ tree.addContainer("", "b");
+ tree.addContainer("a", "a2");
+ tree.addContainer("b", "b2");
+ tree.addContainer("a/a2", "a3");
+ tree.addContainer("b/b2", "b3");
+
+ CParser parser(tree);
+ cd_ expected;
+
+ std::string input;
+
+ SECTION("basic cd parsing")
+ {
+ SECTION("a")
+ {
+ input = "cd a";
+ expected.m_path.m_nodes.push_back(container_("a"));
+ }
+
+ SECTION("b")
+ {
+ input = "cd b";
+ expected.m_path.m_nodes.push_back(container_("b"));
+ }
+
+
+ SECTION("a/a2")
+ {
+ input = "cd a/a2";
+ expected.m_path.m_nodes.push_back(container_("a"));
+ expected.m_path.m_nodes.push_back(container_("a2"));
+ }
+
+ SECTION("b/b2")
+ {
+ input = "cd b/b2";
+ expected.m_path.m_nodes.push_back(container_("b"));
+ expected.m_path.m_nodes.push_back(container_("b2"));
+ }
+
+ cd_ command = parser.parseCommand(input);
+ REQUIRE(command == expected);
+ }
+
+ SECTION("InvalidNodeException")
+ {
+ SECTION("x")
+ {
+ input = "cd x";
+ }
+
+ SECTION("a/x")
+ {
+ input = "cd a/x";
+ }
+ REQUIRE_THROWS_AS(parser.parseCommand(input), InvalidNodeException);
+ }
+}