Allow data path to end with a list for get and ls
Change-Id: I3facc8315fa6192da4318012a85121de37e7314b
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 91380e7..787fb94 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -40,7 +40,7 @@
struct ls_ : x3::position_tagged {
bool operator==(const ls_& b) const;
std::vector<LsOption> m_options;
- boost::optional<dataPath_> m_path;
+ boost::optional<boost::variant<dataPath_, schemaPath_>> m_path;
};
struct cd_ : x3::position_tagged {
@@ -70,7 +70,7 @@
struct get_ : x3::position_tagged {
bool operator==(const get_& b) const;
- boost::optional<dataPath_> m_path;
+ boost::optional<boost::variant<dataPath_, schemaPath_>> m_path;
};
using command_ = boost::variant<discard_, ls_, cd_, create_, delete_, set_, commit_, get_>;
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index c670168..0cb64f1 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -201,6 +201,8 @@
}
};
+struct dataNodeList_class;
+
struct dataNode_class {
template <typename T, typename Iterator, typename Context>
void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
@@ -226,6 +228,10 @@
}
};
+struct dataNodesListEnd_class;
+
+struct dataPathListEnd_class;
+
struct dataPath_class {
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const&, Context const& context)
@@ -332,7 +338,7 @@
leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
if (location.m_nodes.empty()) {
- parserContext.m_curModule = parserContext.m_curPath.m_nodes.back().m_prefix->m_name;
+ parserContext.m_curModule = parserContext.m_curPath.m_nodes.back().m_prefix->m_name;
}
parserContext.m_errorMsg = "Expected " + leafDataTypeToString(schema.leafType(location, {parserContext.m_curModule, leaf.m_name})) + " here:";
return x3::error_handler_result::fail;
@@ -455,3 +461,18 @@
return x3::error_handler_result::fail;
}
};
+
+struct initializeContext_class {
+ template <typename T, typename Iterator, typename Context>
+ void on_success(Iterator const&, Iterator const&, T&, Context const& context)
+ {
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ parserContext.m_curPath = parserContext.m_curPathOrig;
+ parserContext.m_tmpListKeys.clear();
+ parserContext.m_tmpListName.clear();
+ if (!parserContext.m_curPath.m_nodes.empty() && parserContext.m_curPath.m_nodes.at(0).m_prefix)
+ parserContext.m_topLevelModulePresent = true;
+ else
+ parserContext.m_topLevelModulePresent = false;
+ }
+};
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index 56d33c0..1f01a72 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -43,6 +43,11 @@
{
}
+schemaNode_::schemaNode_(decltype(m_suffix) node)
+ : m_suffix(node)
+{
+}
+
schemaNode_::schemaNode_(module_ module, decltype(m_suffix) node)
: m_prefix(module)
, m_suffix(node)
@@ -186,7 +191,7 @@
return res;
}
-std::string pathToSchemaString(const dataPath_& path)
+std::string pathToSchemaString(const schemaPath_& path)
{
std::string res;
for (const auto it : path.m_nodes) {
@@ -198,6 +203,11 @@
return res;
}
+std::string pathToSchemaString(const dataPath_& path)
+{
+ return pathToSchemaString(dataPathToSchemaPath(path));
+}
+
struct dataSuffixToSchemaSuffix : boost::static_visitor<decltype(schemaNode_::m_suffix)> {
auto operator()(const listElement_& listElement) const
{
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index 4fbeaea..2711a30 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -79,7 +79,7 @@
struct dataNode_ {
boost::optional<module_> m_prefix;
- boost::variant<container_, listElement_, nodeup_, leaf_> m_suffix;
+ boost::variant<container_, listElement_, nodeup_, leaf_, list_> m_suffix;
dataNode_();
dataNode_(decltype(m_suffix) node);
@@ -109,7 +109,7 @@
std::string pathToAbsoluteSchemaString(const dataPath_& path);
std::string pathToAbsoluteSchemaString(const schemaPath_& path);
std::string pathToDataString(const dataPath_& path);
-std::string pathToSchemaString(const dataPath_& path);
+std::string pathToSchemaString(const schemaPath_& path);
schemaNode_ dataNodeToSchemaNode(const dataNode_& node);
schemaPath_ dataPathToSchemaPath(const dataPath_& path);
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 7f76ac7..7caa45c 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -28,6 +28,9 @@
x3::rule<schemaNode_class, schemaNode_> const schemaNode = "schemaNode";
x3::rule<absoluteStart_class, Scope> const absoluteStart = "absoluteStart";
x3::rule<schemaPath_class, schemaPath_> const schemaPath = "schemaPath";
+x3::rule<dataNodeList_class, decltype(dataPath_::m_nodes)::value_type> const dataNodeList = "dataNodeList";
+x3::rule<dataNodesListEnd_class, decltype(dataPath_::m_nodes)> const dataNodesListEnd = "dataNodesListEnd";
+x3::rule<dataPathListEnd_class, dataPath_> const dataPathListEnd = "dataPathListEnd";
x3::rule<dataPath_class, dataPath_> const dataPath = "dataPath";
x3::rule<leaf_path_class, dataPath_> const leafPath = "leafPath";
@@ -49,6 +52,8 @@
x3::rule<commit_class, commit_> const commit = "commit";
x3::rule<command_class, command_> const command = "command";
+x3::rule<initializeContext_class, x3::unused_type> const initializeContext = "initializeContext";
+
#if __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Woverloaded-shift-op-parentheses"
@@ -90,7 +95,7 @@
listPrefix > listSuffix;
auto const list_def =
- node_identifier;
+ node_identifier >> !char_('[');
auto const nodeup_def =
lit("..") > x3::attr(nodeup_());
@@ -109,7 +114,7 @@
-(module) >> x3::expect[container | list | nodeup | leaf];
auto const dataNode_def =
- -(module) >> x3::expect[container | listElement | nodeup | leaf];
+ -(module) >> (container | listElement | nodeup | leaf);
auto const absoluteStart_def =
x3::omit['/'] >> x3::attr(Scope::Absolute);
@@ -119,6 +124,21 @@
absoluteStart >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::eoi |
-(absoluteStart) >> dataNode % '/';
+auto const dataNodeList_def =
+ -(module) >> list;
+
+// This intermediate rule is mandatory, because we need the first alternative
+// to be collapsed to a vector. If we didn't use the intermediate rule,
+// Spirit wouldn't know we want it to collapse.
+// https://github.com/boostorg/spirit/issues/408
+auto const dataNodesListEnd_def =
+ dataNode % '/' >> '/' >> dataNodeList |
+ initializeContext >> x3::attr(decltype(dataPath_::m_nodes)()) >> dataNodeList;
+
+auto const dataPathListEnd_def =
+ absoluteStart >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::eoi |
+ -(absoluteStart) >> dataNodesListEnd;
+
auto const schemaPath_def =
absoluteStart >> x3::attr(decltype(schemaPath_::m_nodes)()) >> x3::eoi |
-(absoluteStart) >> schemaNode % '/';
@@ -169,8 +189,12 @@
}
} const ls_options;
+// A "nothing" parser, which is used to reset the context (when trying to parse different types of paths)
+auto const initializeContext_def =
+ x3::eps;
+
auto const ls_def =
- lit("ls") >> *(space_separator >> ls_options) >> -(space_separator >> dataPath);
+ lit("ls") >> *(space_separator >> ls_options) >> -(space_separator >> (dataPathListEnd | initializeContext >> dataPath));
auto const cd_def =
lit("cd") >> space_separator > dataPath;
@@ -182,7 +206,7 @@
lit("delete") >> space_separator > dataPath;
auto const get_def =
- lit("get") >> -dataPath;
+ lit("get") >> -(space_separator >> (dataPathListEnd | initializeContext >> dataPath));
auto const set_def =
lit("set") >> space_separator > leafPath > leaf_data;
@@ -216,6 +240,9 @@
BOOST_SPIRIT_DEFINE(leafPath)
BOOST_SPIRIT_DEFINE(schemaPath)
BOOST_SPIRIT_DEFINE(dataPath)
+BOOST_SPIRIT_DEFINE(dataNodeList)
+BOOST_SPIRIT_DEFINE(dataNodesListEnd)
+BOOST_SPIRIT_DEFINE(dataPathListEnd)
BOOST_SPIRIT_DEFINE(absoluteStart)
BOOST_SPIRIT_DEFINE(module)
BOOST_SPIRIT_DEFINE(leaf_data)
@@ -225,6 +252,7 @@
BOOST_SPIRIT_DEFINE(leaf_data_int)
BOOST_SPIRIT_DEFINE(leaf_data_uint)
BOOST_SPIRIT_DEFINE(leaf_data_string)
+BOOST_SPIRIT_DEFINE(initializeContext)
BOOST_SPIRIT_DEFINE(set)
BOOST_SPIRIT_DEFINE(commit)
BOOST_SPIRIT_DEFINE(get)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 452bc74..80e6195 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -84,14 +84,39 @@
return joinPaths(m_parser.currentNode(), pathToDataString(command.m_path));
}
+struct pathToStringVisitor : boost::static_visitor<std::string> {
+ std::string operator()(const schemaPath_& path) const
+ {
+ return pathToSchemaString(path);
+ }
+ std::string operator()(const dataPath_& path) const
+ {
+ return pathToDataString(path);
+ }
+};
+
+struct getPathScopeVisitor : boost::static_visitor<Scope> {
+ template <typename T>
+ Scope operator()(const T& path) const
+ {
+ return path.m_scope;
+ }
+};
+
std::string Interpreter::absolutePathFromCommand(const get_& get) const
{
if (!get.m_path) {
return m_parser.currentNode();
- } else if (get.m_path->m_scope == Scope::Absolute) {
- return "/" + pathToDataString(*get.m_path);
+ }
+
+ const auto path = *get.m_path;
+ std::string pathString = boost::apply_visitor(pathToStringVisitor(), path);
+ auto pathScope{boost::apply_visitor(getPathScopeVisitor(), path)};
+
+ if (pathScope == Scope::Absolute) {
+ return "/" + pathString;
} else {
- return joinPaths(m_parser.currentNode(), pathToDataString(*get.m_path));
+ return joinPaths(m_parser.currentNode(), pathString);
}
}
diff --git a/src/parser.cpp b/src/parser.cpp
index bb17495..ee0233e 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -70,11 +70,12 @@
}
};
-std::set<std::string> Parser::availableNodes(const boost::optional<dataPath_>& path, const Recursion& option) const
+
+std::set<std::string> Parser::availableNodes(const boost::optional<boost::variant<dataPath_, schemaPath_>>& path, const Recursion& option) const
{
auto pathArg = dataPathToSchemaPath(m_curDir);
if (path) {
- auto schemaPath = dataPathToSchemaPath(*path);
+ auto schemaPath = boost::apply_visitor(getSchemaPathVisitor(), *path);
pathArg.m_nodes.insert(pathArg.m_nodes.end(), schemaPath.m_nodes.begin(), schemaPath.m_nodes.end());
}
return m_schema->childNodes(pathArg, option);
diff --git a/src/parser.hpp b/src/parser.hpp
index 013a2f4..343a1e5 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -29,7 +29,7 @@
command_ parseCommand(const std::string& line, std::ostream& errorStream);
void changeNode(const dataPath_& name);
std::string currentNode() const;
- std::set<std::string> availableNodes(const boost::optional<dataPath_>& path, const Recursion& option) const;
+ std::set<std::string> availableNodes(const boost::optional<boost::variant<dataPath_, schemaPath_>>& path, const Recursion& option) const;
private:
const std::shared_ptr<const Schema> m_schema;
diff --git a/src/parser_context.cpp b/src/parser_context.cpp
index f5e2d32..c240984 100644
--- a/src/parser_context.cpp
+++ b/src/parser_context.cpp
@@ -9,9 +9,9 @@
#include "parser_context.hpp"
ParserContext::ParserContext(const Schema& schema, const schemaPath_& curDir)
: m_schema(schema)
+ , m_curPath(curDir)
+ , m_curPathOrig(curDir)
{
- m_curPath = curDir;
-
if (!m_curPath.m_nodes.empty() && m_curPath.m_nodes.at(0).m_prefix)
m_topLevelModulePresent = true;
}
diff --git a/src/parser_context.hpp b/src/parser_context.hpp
index 01ab7e8..a0b8e30 100644
--- a/src/parser_context.hpp
+++ b/src/parser_context.hpp
@@ -11,6 +11,7 @@
ParserContext(const Schema& schema, const schemaPath_& curDir);
const Schema& m_schema;
schemaPath_ m_curPath;
+ const schemaPath_ m_curPathOrig;
boost::optional<std::string> m_curModule;
std::string m_errorMsg;
std::string m_tmpListName;
diff --git a/tests/ls.cpp b/tests/ls.cpp
index d202a1c..e42152d 100644
--- a/tests/ls.cpp
+++ b/tests/ls.cpp
@@ -17,6 +17,7 @@
schema->addModule("example");
schema->addModule("second");
schema->addContainer("", "example:a");
+ schema->addList("example:a", "example:listInCont", {"number"});
schema->addContainer("", "second:a");
schema->addContainer("", "example:b");
schema->addContainer("example:a", "example:a2");
@@ -127,6 +128,28 @@
expected.m_options.push_back(LsOption::Recursive);
expected.m_path = dataPath_{Scope::Absolute, {dataNode_(module_{"example"}, container_{"a"})}};
}
+
+ SECTION("ls example:list")
+ {
+ input = "ls example:list";
+ expected.m_path = dataPath_{Scope::Relative, {dataNode_(module_{"example"}, list_{"list"})}};
+ }
+
+ SECTION("ls example:a/example:listInCont")
+ {
+ input = "ls example:a/example:listInCont";
+ expected.m_path = dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"}),
+ dataNode_(module_{"example"}, list_{"listInCont"})}};
+ }
+
+ SECTION("ls example:list[number=342]/contInList")
+ {
+ input = "ls example:list[number=342]/contInList";
+ auto keys = std::map<std::string, std::string>{
+ {"number", "342"}};
+ expected.m_path = dataPath_{Scope::Relative, {dataNode_(module_{"example"}, listElement_{"list", keys}),
+ dataNode_(container_{"contInList"})}};
+ }
}
command_ command = parser.parseCommand(input, errorStream);