Split path_ into schemaPath_ and dataPath_

This change is necessary because different commands accept different
kinds of paths (for example "cd" only accepts a data path, on the other
hand "ls" doesn't care about data, so it accepts both). One option was to
create a new path struct for every command, but that could get quickly
out of control as new commands get added. The other option was define only
the data path and schema path and then change the commands' grammars, so
that they only accept the relevant paths, but in the end always return a
data path or a schema path.

Change-Id: I7668a446fbf674c7a5deae22d9aacdfb3da9b07e
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index a1c377b..91380e7 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -40,27 +40,27 @@
 struct ls_ : x3::position_tagged {
     bool operator==(const ls_& b) const;
     std::vector<LsOption> m_options;
-    boost::optional<path_> m_path;
+    boost::optional<dataPath_> m_path;
 };
 
 struct cd_ : x3::position_tagged {
     bool operator==(const cd_& b) const;
-    path_ m_path;
+    dataPath_ m_path;
 };
 
 struct create_ : x3::position_tagged {
     bool operator==(const create_& b) const;
-    path_ m_path;
+    dataPath_ m_path;
 };
 
 struct delete_ : x3::position_tagged {
     bool operator==(const delete_& b) const;
-    path_ m_path;
+    dataPath_ m_path;
 };
 
 struct set_ : x3::position_tagged {
     bool operator==(const set_& b) const;
-    path_ m_path;
+    dataPath_ m_path;
     leaf_data_ m_data;
 };
 
@@ -70,7 +70,7 @@
 
 struct get_ : x3::position_tagged {
     bool operator==(const get_& b) const;
-    boost::optional<path_> m_path;
+    boost::optional<dataPath_> 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 b19ece0..c670168 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -121,7 +121,18 @@
         }
     }
 };
+struct list_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 Schema& schema = parserContext.m_schema;
 
+        if (!schema.isList(parserContext.m_curPath, {parserContext.m_curModule, ast.m_name})) {
+            _pass(context) = false;
+        }
+    }
+};
 struct nodeup_class {
     template <typename T, typename Iterator, typename Context>
     void on_success(Iterator const&, Iterator const&, T&, Context const& context)
@@ -157,7 +168,6 @@
     }
 };
 
-
 struct module_class {
     template <typename T, typename Iterator, typename Context>
     void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
@@ -175,7 +185,7 @@
     }
 };
 
-struct node_class {
+struct schemaNode_class {
     template <typename T, typename Iterator, typename Context>
     void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
     {
@@ -191,6 +201,22 @@
     }
 };
 
+struct dataNode_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);
+        if (ast.m_suffix.type() == typeid(nodeup_)) {
+            parserContext.m_curPath.m_nodes.pop_back();
+            if (parserContext.m_curPath.m_nodes.empty())
+                parserContext.m_topLevelModulePresent = false;
+        } else {
+            parserContext.m_curPath.m_nodes.push_back(dataNodeToSchemaNode(ast));
+            parserContext.m_curModule = boost::none;
+        }
+    }
+};
+
 struct absoluteStart_class {
     template <typename T, typename Iterator, typename Context>
     void on_success(Iterator const&, Iterator const&, T&, Context const& context)
@@ -200,7 +226,21 @@
     }
 };
 
-struct path_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)
+    {
+        auto& parserContext = x3::get<parser_context_tag>(context);
+        if (parserContext.m_errorMsg.empty()) {
+            parserContext.m_errorMsg = "Expected path.";
+            return x3::error_handler_result::fail;
+        } else {
+            return x3::error_handler_result::rethrow;
+        }
+    }
+};
+
+struct schemaPath_class {
     template <typename Iterator, typename Exception, typename Context>
     x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const&, Context const& context)
     {
@@ -240,7 +280,7 @@
             if (ast.m_path.m_nodes.back().m_prefix)
                 module = ast.m_path.m_nodes.back().m_prefix.value().m_name;
             container_ cont = boost::get<container_>(ast.m_path.m_nodes.back().m_suffix);
-            path_ location = pathWithoutLastNode(parserContext.m_curPath);
+            schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
 
             if (!schema.isPresenceContainer(location, {module, cont.m_name})) {
                 parserContext.m_errorMsg = "This container is not a presence container.";
@@ -290,7 +330,10 @@
         auto& schema = parserContext.m_schema;
         if (parserContext.m_errorMsg.empty()) {
             leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
-            path_ location = pathWithoutLastNode(parserContext.m_curPath);
+            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_errorMsg = "Expected " + leafDataTypeToString(schema.leafType(location, {parserContext.m_curModule, leaf.m_name})) + " here:";
             return x3::error_handler_result::fail;
         }
@@ -316,7 +359,7 @@
             module = parserContext.m_curPath.m_nodes.back().m_prefix.value().m_name;
 
         leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
-        path_ location = pathWithoutLastNode(parserContext.m_curPath);
+        schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
 
         if (schema.leafType(location, {module, leaf.m_name}) != m_type) {
             _pass(context) = false;
@@ -341,7 +384,7 @@
             module = parserContext.m_curPath.m_nodes.back().m_prefix.value().m_name;
 
         leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
-        path_ location = pathWithoutLastNode(parserContext.m_curPath);
+        schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
 
         if (!schema.leafEnumHasValue(location, {module, leaf.m_name}, ast.m_value)) {
             _pass(context) = false;
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index bb14425..56d33c0 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -30,21 +30,33 @@
     return this->m_name == b.m_name;
 }
 
-node_::node_() = default;
+dataNode_::dataNode_() = default;
 
-node_::node_(decltype(m_suffix) node)
+dataNode_::dataNode_(decltype(m_suffix) node)
     : m_suffix(node)
 {
 }
 
-node_::node_(module_ module, decltype(m_suffix) node)
+dataNode_::dataNode_(module_ module, decltype(m_suffix) node)
     : m_prefix(module)
     , m_suffix(node)
 {
 }
 
+schemaNode_::schemaNode_(module_ module, decltype(m_suffix) node)
+    : m_prefix(module)
+    , m_suffix(node)
+{
+}
 
-bool node_::operator==(const node_& b) const
+schemaNode_::schemaNode_() = default;
+
+bool schemaNode_::operator==(const schemaNode_& b) const
+{
+    return this->m_suffix == b.m_suffix && this->m_prefix == b.m_prefix;
+}
+
+bool dataNode_::operator==(const dataNode_& b) const
 {
     return this->m_suffix == b.m_suffix && this->m_prefix == b.m_prefix;
 }
@@ -65,7 +77,24 @@
     return (this->m_name == b.m_name && this->m_keys == b.m_keys);
 }
 
-bool path_::operator==(const path_& b) const
+bool list_::operator==(const list_& b) const
+{
+    return (this->m_name == b.m_name);
+}
+
+list_::list_(const std::string& listName)
+    : m_name(listName)
+{
+}
+
+bool schemaPath_::operator==(const schemaPath_& b) const
+{
+    if (this->m_nodes.size() != b.m_nodes.size())
+        return false;
+    return this->m_nodes == b.m_nodes;
+}
+
+bool dataPath_::operator==(const dataPath_& b) const
 {
     if (this->m_nodes.size() != b.m_nodes.size())
         return false;
@@ -118,12 +147,12 @@
     }
 };
 
-std::string nodeToSchemaString(decltype(path_::m_nodes)::value_type node)
+std::string nodeToSchemaString(decltype(dataPath_::m_nodes)::value_type node)
 {
     return boost::apply_visitor(nodeToSchemaStringVisitor(), node.m_suffix);
 }
 
-std::string pathToDataString(const path_& path)
+std::string pathToDataString(const dataPath_& path)
 {
     std::string res;
     for (const auto it : path.m_nodes)
@@ -135,7 +164,12 @@
     return res;
 }
 
-std::string pathToAbsoluteSchemaString(const path_& path)
+std::string pathToAbsoluteSchemaString(const dataPath_& path)
+{
+    return pathToAbsoluteSchemaString(dataPathToSchemaPath(path));
+}
+
+std::string pathToAbsoluteSchemaString(const schemaPath_& path)
 {
     std::string res;
     if (path.m_nodes.empty()) {
@@ -152,7 +186,7 @@
     return res;
 }
 
-std::string pathToSchemaString(const path_& path)
+std::string pathToSchemaString(const dataPath_& path)
 {
     std::string res;
     for (const auto it : path.m_nodes) {
@@ -163,3 +197,35 @@
     }
     return res;
 }
+
+struct dataSuffixToSchemaSuffix : boost::static_visitor<decltype(schemaNode_::m_suffix)> {
+    auto operator()(const listElement_& listElement) const
+    {
+        return list_{listElement.m_name};
+    }
+
+    template <typename T>
+    auto operator()(const T& suffix) const
+    {
+        return suffix;
+    }
+};
+
+schemaNode_ dataNodeToSchemaNode(const dataNode_& node)
+{
+    schemaNode_ res;
+    res.m_prefix = node.m_prefix;
+    res.m_suffix = boost::apply_visitor(dataSuffixToSchemaSuffix(), node.m_suffix);
+    return res;
+}
+
+schemaPath_ dataPathToSchemaPath(const dataPath_& path)
+{
+        schemaPath_ res{path.m_scope, {}};
+
+        std::transform(path.m_nodes.begin(), path.m_nodes.end(),
+                       std::back_inserter(res.m_nodes),
+                       [](const dataNode_& node) { return dataNodeToSchemaNode(node); });
+
+        return res;
+}
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index dab06ea..4fbeaea 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -53,19 +53,38 @@
     std::map<std::string, std::string> m_keys;
 };
 
+struct list_ {
+    list_() {}
+    list_(const std::string& listName);
+
+    bool operator==(const list_& b) const;
+
+    std::string m_name;
+};
+
 struct module_ {
     bool operator==(const module_& b) const;
     std::string m_name;
 };
 
-struct node_ {
+struct schemaNode_ {
+    boost::optional<module_> m_prefix;
+    boost::variant<container_, list_, nodeup_, leaf_> m_suffix;
+
+    schemaNode_();
+    schemaNode_(decltype(m_suffix) node);
+    schemaNode_(module_ module, decltype(m_suffix) node);
+    bool operator==(const schemaNode_& b) const;
+};
+
+struct dataNode_ {
     boost::optional<module_> m_prefix;
     boost::variant<container_, listElement_, nodeup_, leaf_> m_suffix;
 
-    node_();
-    node_(decltype(m_suffix) node);
-    node_(module_ module, decltype(m_suffix) node);
-    bool operator==(const node_& b) const;
+    dataNode_();
+    dataNode_(decltype(m_suffix) node);
+    dataNode_(module_ module, decltype(m_suffix) node);
+    bool operator==(const dataNode_& b) const;
 };
 
 enum class Scope {
@@ -73,20 +92,31 @@
     Relative
 };
 
-struct path_ {
-    bool operator==(const path_& b) const;
+struct schemaPath_ {
+    bool operator==(const schemaPath_& b) const;
     Scope m_scope = Scope::Relative;
-    std::vector<node_> m_nodes;
+    std::vector<schemaNode_> m_nodes;
 };
 
-std::string nodeToSchemaString(decltype(path_::m_nodes)::value_type node);
+struct dataPath_ {
+    bool operator==(const dataPath_& b) const;
+    Scope m_scope = Scope::Relative;
+    std::vector<dataNode_> m_nodes;
+};
 
-std::string pathToAbsoluteSchemaString(const path_& path);
-std::string pathToDataString(const path_& path);
-std::string pathToSchemaString(const path_& path);
+std::string nodeToSchemaString(decltype(dataPath_::m_nodes)::value_type node);
+
+std::string pathToAbsoluteSchemaString(const dataPath_& path);
+std::string pathToAbsoluteSchemaString(const schemaPath_& path);
+std::string pathToDataString(const dataPath_& path);
+std::string pathToSchemaString(const dataPath_& path);
+schemaNode_ dataNodeToSchemaNode(const dataNode_& node);
+schemaPath_ dataPathToSchemaPath(const dataPath_& path);
 
 BOOST_FUSION_ADAPT_STRUCT(container_, m_name)
 BOOST_FUSION_ADAPT_STRUCT(listElement_, m_name, m_keys)
 BOOST_FUSION_ADAPT_STRUCT(module_, m_name)
-BOOST_FUSION_ADAPT_STRUCT(node_, m_prefix, m_suffix)
-BOOST_FUSION_ADAPT_STRUCT(path_, m_scope, m_nodes)
+BOOST_FUSION_ADAPT_STRUCT(dataNode_, m_prefix, m_suffix)
+BOOST_FUSION_ADAPT_STRUCT(schemaNode_, m_prefix, m_suffix)
+BOOST_FUSION_ADAPT_STRUCT(dataPath_, m_scope, m_nodes)
+BOOST_FUSION_ADAPT_STRUCT(schemaPath_, m_scope, m_nodes)
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 03753e8..7f76ac7 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -19,14 +19,17 @@
 x3::rule<listPrefix_class, std::string> const listPrefix = "listPrefix";
 x3::rule<listSuffix_class, std::vector<keyValue_>> const listSuffix = "listSuffix";
 x3::rule<listElement_class, listElement_> const listElement = "listElement";
+x3::rule<list_class, list_> const list = "list";
 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<module_class, module_> const module = "module";
-x3::rule<node_class, node_> const node = "node";
+x3::rule<dataNode_class, dataNode_> const dataNode = "dataNode";
+x3::rule<schemaNode_class, schemaNode_> const schemaNode = "schemaNode";
 x3::rule<absoluteStart_class, Scope> const absoluteStart = "absoluteStart";
-x3::rule<path_class, path_> const path = "path";
-x3::rule<leaf_path_class, path_> const leafPath = "leafPath";
+x3::rule<schemaPath_class, schemaPath_> const schemaPath = "schemaPath";
+x3::rule<dataPath_class, dataPath_> const dataPath = "dataPath";
+x3::rule<leaf_path_class, dataPath_> const leafPath = "leafPath";
 
 x3::rule<leaf_data_class, leaf_data_> const leaf_data = "leaf_data";
 x3::rule<leaf_data_enum_class, enum_> const leaf_data_enum = "leaf_data_enum";
@@ -86,6 +89,9 @@
 auto const listElement_def =
         listPrefix > listSuffix;
 
+auto const list_def =
+        node_identifier;
+
 auto const nodeup_def =
         lit("..") > x3::attr(nodeup_());
 
@@ -99,19 +105,26 @@
         node_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 node_def =
+auto const schemaNode_def =
+        -(module) >> x3::expect[container | list | nodeup | leaf];
+
+auto const dataNode_def =
         -(module) >> x3::expect[container | listElement | nodeup | leaf];
 
 auto const absoluteStart_def =
         x3::omit['/'] >> x3::attr(Scope::Absolute);
 
 // I have to insert an empty vector to the first alternative, otherwise they won't have the same attribute
-auto const path_def =
-        absoluteStart >> x3::attr(decltype(path_::m_nodes)()) >> x3::eoi |
-        -(absoluteStart) >> node % '/';
+auto const dataPath_def =
+        absoluteStart >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::eoi |
+        -(absoluteStart) >> dataNode % '/';
+
+auto const schemaPath_def =
+        absoluteStart >> x3::attr(decltype(schemaPath_::m_nodes)()) >> x3::eoi |
+        -(absoluteStart) >> schemaNode % '/';
 
 auto const leafPath_def =
-        path;
+        dataPath;
 
 auto const leaf_data_enum_def =
         +char_;
@@ -157,19 +170,19 @@
 } const ls_options;
 
 auto const ls_def =
-        lit("ls") >> *(space_separator >> ls_options) >> -(space_separator >> path);
+        lit("ls") >> *(space_separator >> ls_options) >> -(space_separator >> dataPath);
 
 auto const cd_def =
-        lit("cd") >> space_separator > path;
+        lit("cd") >> space_separator > dataPath;
 
 auto const create_def =
-        lit("create") >> space_separator > path;
+        lit("create") >> space_separator > dataPath;
 
 auto const delete_rule_def =
-        lit("delete") >> space_separator > path;
+        lit("delete") >> space_separator > dataPath;
 
 auto const get_def =
-        lit("get") >> -path;
+        lit("get") >> -dataPath;
 
 auto const set_def =
         lit("set") >> space_separator > leafPath > leaf_data;
@@ -194,13 +207,16 @@
 BOOST_SPIRIT_DEFINE(listPrefix)
 BOOST_SPIRIT_DEFINE(listSuffix)
 BOOST_SPIRIT_DEFINE(listElement)
+BOOST_SPIRIT_DEFINE(list)
 BOOST_SPIRIT_DEFINE(nodeup)
+BOOST_SPIRIT_DEFINE(schemaNode)
+BOOST_SPIRIT_DEFINE(dataNode)
 BOOST_SPIRIT_DEFINE(container)
 BOOST_SPIRIT_DEFINE(leaf)
 BOOST_SPIRIT_DEFINE(leafPath)
-BOOST_SPIRIT_DEFINE(node)
+BOOST_SPIRIT_DEFINE(schemaPath)
+BOOST_SPIRIT_DEFINE(dataPath)
 BOOST_SPIRIT_DEFINE(absoluteStart)
-BOOST_SPIRIT_DEFINE(path)
 BOOST_SPIRIT_DEFINE(module)
 BOOST_SPIRIT_DEFINE(leaf_data)
 BOOST_SPIRIT_DEFINE(leaf_data_enum)
diff --git a/src/parser.cpp b/src/parser.cpp
index f25f1a3..bb17495 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -21,7 +21,7 @@
 command_ Parser::parseCommand(const std::string& line, std::ostream& errorStream)
 {
     command_ parsedCommand;
-    ParserContext ctx(*m_schema, m_curDir);
+    ParserContext ctx(*m_schema, dataPathToSchemaPath(m_curDir));
     auto it = line.begin();
 
     boost::spirit::x3::error_handler<std::string::const_iterator> errorHandler(it, line.end(), errorStream);
@@ -39,7 +39,7 @@
     return parsedCommand;
 }
 
-void Parser::changeNode(const path_& name)
+void Parser::changeNode(const dataPath_& name)
 {
     if (name.m_scope == Scope::Absolute) {
         m_curDir = name;
@@ -58,10 +58,24 @@
     return "/" + pathToDataString(m_curDir);
 }
 
-std::set<std::string> Parser::availableNodes(const boost::optional<path_>& path, const Recursion& option) const
+struct getSchemaPathVisitor : boost::static_visitor<schemaPath_> {
+    schemaPath_ operator()(const dataPath_& path) const
+    {
+        return dataPathToSchemaPath(path);
+    }
+
+    schemaPath_ operator()(const schemaPath_& path) const
+    {
+        return path;
+    }
+};
+
+std::set<std::string> Parser::availableNodes(const boost::optional<dataPath_>& path, const Recursion& option) const
 {
-    auto pathArg = m_curDir;
-    if (path)
-        pathArg.m_nodes.insert(pathArg.m_nodes.end(), path->m_nodes.begin(), path->m_nodes.end());
+    auto pathArg = dataPathToSchemaPath(m_curDir);
+    if (path) {
+        auto schemaPath = dataPathToSchemaPath(*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 0318733..013a2f4 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -27,11 +27,11 @@
 public:
     Parser(const std::shared_ptr<const Schema> schema);
     command_ parseCommand(const std::string& line, std::ostream& errorStream);
-    void changeNode(const path_& name);
+    void changeNode(const dataPath_& name);
     std::string currentNode() const;
-    std::set<std::string> availableNodes(const boost::optional<path_>& path, const Recursion& option) const;
+    std::set<std::string> availableNodes(const boost::optional<dataPath_>& path, const Recursion& option) const;
 
 private:
     const std::shared_ptr<const Schema> m_schema;
-    path_ m_curDir;
+    dataPath_ m_curDir;
 };
diff --git a/src/parser_context.cpp b/src/parser_context.cpp
index fa64344..f5e2d32 100644
--- a/src/parser_context.cpp
+++ b/src/parser_context.cpp
@@ -7,7 +7,7 @@
 */
 
 #include "parser_context.hpp"
-ParserContext::ParserContext(const Schema& schema, const path_ curDir)
+ParserContext::ParserContext(const Schema& schema, const schemaPath_& curDir)
     : m_schema(schema)
 {
     m_curPath = curDir;
diff --git a/src/parser_context.hpp b/src/parser_context.hpp
index 28c5eb1..01ab7e8 100644
--- a/src/parser_context.hpp
+++ b/src/parser_context.hpp
@@ -8,9 +8,9 @@
 
 #include "schema.hpp"
 struct ParserContext {
-    ParserContext(const Schema& schema, const path_ curDir);
+    ParserContext(const Schema& schema, const schemaPath_& curDir);
     const Schema& m_schema;
-    path_ m_curPath;
+    schemaPath_ m_curPath;
     boost::optional<std::string> m_curModule;
     std::string m_errorMsg;
     std::string m_tmpListName;
diff --git a/src/schema.hpp b/src/schema.hpp
index e5827de..e1f019f 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -69,17 +69,17 @@
 public:
     virtual ~Schema();
 
-    virtual bool isContainer(const path_& location, const ModuleNodePair& node) const = 0;
-    virtual bool isLeaf(const path_& location, const ModuleNodePair& node) const = 0;
-    virtual bool isModule(const path_& location, const std::string& name) const = 0;
-    virtual bool isList(const path_& location, const ModuleNodePair& node) const = 0;
-    virtual bool isPresenceContainer(const path_& location, const ModuleNodePair& node) const = 0;
-    virtual bool leafEnumHasValue(const path_& location, const ModuleNodePair& node, const std::string& value) const = 0;
-    virtual bool listHasKey(const path_& location, const ModuleNodePair& node, const std::string& key) const = 0;
+    virtual bool isContainer(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+    virtual bool isLeaf(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+    virtual bool isModule(const schemaPath_& location, const std::string& name) const = 0;
+    virtual bool isList(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+    virtual bool isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+    virtual bool leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const = 0;
+    virtual bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const = 0;
     virtual bool nodeExists(const std::string& location, const std::string& node) const = 0;
-    virtual const std::set<std::string> listKeys(const path_& location, const ModuleNodePair& node) const = 0;
-    virtual yang::LeafDataTypes leafType(const path_& location, const ModuleNodePair& node) const = 0;
-    virtual std::set<std::string> childNodes(const path_& path, const Recursion recursion) const = 0;
+    virtual const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+    virtual yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+    virtual std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const = 0;
 
 private:
     const std::unordered_map<std::string, NodeType>& children(const std::string& name) const;
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index ce94218..9be7751 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -31,12 +31,12 @@
     return childrenRef.find(node) != childrenRef.end();
 }
 
-bool StaticSchema::isModule(const path_&, const std::string& name) const
+bool StaticSchema::isModule(const schemaPath_&, const std::string& name) const
 {
     return m_modules.find(name) != m_modules.end();
 }
 
-bool StaticSchema::isContainer(const path_& location, const ModuleNodePair& node) const
+bool StaticSchema::isContainer(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string locationString = pathToAbsoluteSchemaString(location);
     auto fullName = fullNodeName(location, node);
@@ -55,7 +55,7 @@
     m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
 }
 
-bool StaticSchema::listHasKey(const path_& location, const ModuleNodePair& node, const std::string& key) const
+bool StaticSchema::listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const
 {
     std::string locationString = pathToAbsoluteSchemaString(location);
     assert(isList(location, node));
@@ -65,7 +65,7 @@
     return list.m_keys.find(key) != list.m_keys.end();
 }
 
-const std::set<std::string> StaticSchema::listKeys(const path_& location, const ModuleNodePair& node) const
+const std::set<std::string> StaticSchema::listKeys(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string locationString = pathToAbsoluteSchemaString(location);
     assert(isList(location, node));
@@ -75,7 +75,7 @@
     return list.m_keys;
 }
 
-bool StaticSchema::isList(const path_& location, const ModuleNodePair& node) const
+bool StaticSchema::isList(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string locationString = pathToAbsoluteSchemaString(location);
     auto fullName = fullNodeName(location, node);
@@ -95,7 +95,7 @@
     m_nodes.emplace(name, std::unordered_map<std::string, NodeType>());
 }
 
-bool StaticSchema::isPresenceContainer(const path_& location, const ModuleNodePair& node) const
+bool StaticSchema::isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const
 {
     if (!isContainer(location, node))
         return false;
@@ -119,7 +119,7 @@
 }
 
 
-bool StaticSchema::leafEnumHasValue(const path_& location, const ModuleNodePair& node, const std::string& value) const
+bool StaticSchema::leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const
 {
     std::string locationString = pathToAbsoluteSchemaString(location);
     assert(isLeaf(location, node));
@@ -129,7 +129,7 @@
     return list.m_enumValues.find(value) != list.m_enumValues.end();
 }
 
-bool StaticSchema::isLeaf(const path_& location, const ModuleNodePair& node) const
+bool StaticSchema::isLeaf(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string locationString = pathToAbsoluteSchemaString(location);
     auto fullName = fullNodeName(location, node);
@@ -139,7 +139,7 @@
     return children(locationString).at(fullName).type() == typeid(yang::leaf);
 }
 
-yang::LeafDataTypes StaticSchema::leafType(const path_& location, const ModuleNodePair& node) const
+yang::LeafDataTypes StaticSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string locationString = pathToAbsoluteSchemaString(location);
     return boost::get<yang::leaf>(children(locationString).at(fullNodeName(location, node))).m_type;
@@ -147,7 +147,7 @@
 
 // We do not test StaticSchema, so we don't need to implement recursive childNodes
 // for this class.
-std::set<std::string> StaticSchema::childNodes(const path_& path, const Recursion) const
+std::set<std::string> StaticSchema::childNodes(const schemaPath_& path, const Recursion) const
 {
     std::string locationString = pathToAbsoluteSchemaString(path);
     std::set<std::string> res;
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index 68f1401..7326120 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -23,17 +23,17 @@
 public:
     StaticSchema();
 
-    bool isContainer(const path_& location, const ModuleNodePair& node) const override;
-    bool isModule(const path_& location, const std::string& name) const override;
-    bool isLeaf(const path_& location, const ModuleNodePair& node) const override;
-    bool isList(const path_& location, const ModuleNodePair& node) const override;
-    bool isPresenceContainer(const path_& location, const ModuleNodePair& node) const override;
-    bool leafEnumHasValue(const path_& location, const ModuleNodePair& node, const std::string& value) const override;
-    bool listHasKey(const path_& location, const ModuleNodePair& node, const std::string& key) const override;
+    bool isContainer(const schemaPath_& location, const ModuleNodePair& node) const override;
+    bool isModule(const schemaPath_& location, const std::string& name) const override;
+    bool isLeaf(const schemaPath_& location, const ModuleNodePair& node) const override;
+    bool isList(const schemaPath_& location, const ModuleNodePair& node) const override;
+    bool isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const override;
+    bool leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const override;
+    bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const override;
     bool nodeExists(const std::string& location, const std::string& node) const override;
-    const std::set<std::string> listKeys(const path_& location, const ModuleNodePair& node) const override;
-    yang::LeafDataTypes leafType(const path_& location, const ModuleNodePair& node) const override;
-    std::set<std::string> childNodes(const path_& path, const Recursion) const override;
+    const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
+    yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
+    std::set<std::string> childNodes(const schemaPath_& path, const Recursion) const override;
 
     void addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence = yang::ContainerTraits::None);
     void addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type);
diff --git a/src/utils.cpp b/src/utils.cpp
index eb48349..f9eae06 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -26,9 +26,9 @@
     return res;
 }
 
-path_ pathWithoutLastNode(const path_& path)
+schemaPath_ pathWithoutLastNode(const schemaPath_& path)
 {
-    return path_{path.m_scope, decltype(path_::m_nodes)(path.m_nodes.begin(), path.m_nodes.end() - 1)};
+    return schemaPath_{path.m_scope, decltype(schemaPath_::m_nodes)(path.m_nodes.begin(), path.m_nodes.end() - 1)};
 }
 
 std::string leafDataTypeToString(yang::LeafDataTypes type)
@@ -51,7 +51,7 @@
     }
 }
 
-std::string fullNodeName(const path_& location, const ModuleNodePair& pair)
+std::string fullNodeName(const schemaPath_& location, const ModuleNodePair& pair)
 {
     if (!pair.first) {
         return location.m_nodes.at(0).m_prefix.value().m_name + ":" + pair.second;
@@ -59,3 +59,8 @@
         return pair.first.value() + ":" + pair.second;
     }
 }
+
+std::string fullNodeName(const dataPath_& location, const ModuleNodePair& pair)
+{
+    return fullNodeName(dataPathToSchemaPath(location), pair);
+}
diff --git a/src/utils.hpp b/src/utils.hpp
index 49b5b29..f009ccd 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -11,6 +11,8 @@
 
 std::string joinPaths(const std::string& prefix, const std::string& suffix);
 std::string stripLastNodeFromPath(const std::string& path);
-path_ pathWithoutLastNode(const path_& path);
+schemaPath_ pathWithoutLastNode(const schemaPath_& path);
+dataPath_ pathWithoutLastNode(const dataPath_& path);
 std::string leafDataTypeToString(yang::LeafDataTypes type);
-std::string fullNodeName(const path_& location, const ModuleNodePair& pair);
+std::string fullNodeName(const schemaPath_& location, const ModuleNodePair& pair);
+std::string fullNodeName(const dataPath_& location, const ModuleNodePair& pair);
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 7127082..522e58a 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -30,7 +30,8 @@
     ~InvalidSchemaQueryException() override = default;
 };
 
-std::string pathToYangAbsSchemPath(const path_& path)
+template <typename T>
+std::string pathToYangAbsSchemPath(const T& path)
 {
     std::string res = "/";
     std::string currentModule;
@@ -78,38 +79,38 @@
     }
 }
 
-bool YangSchema::isModule(const path_&, const std::string& name) const
+bool YangSchema::isModule(const schemaPath_&, const std::string& name) const
 {
     const auto set = modules();
     return set.find(name) != set.end();
 }
 
-bool YangSchema::isContainer(const path_& location, const ModuleNodePair& node) const
+bool YangSchema::isContainer(const schemaPath_& location, const ModuleNodePair& node) const
 {
     const auto schemaNode = getSchemaNode(location, node);
     return schemaNode && schemaNode->nodetype() == LYS_CONTAINER;
 }
 
-bool YangSchema::isLeaf(const path_& location, const ModuleNodePair& node) const
+bool YangSchema::isLeaf(const schemaPath_& location, const ModuleNodePair& node) const
 {
     const auto schemaNode = getSchemaNode(location, node);
     return schemaNode && schemaNode->nodetype() == LYS_LEAF;
 }
 
-bool YangSchema::isList(const path_& location, const ModuleNodePair& node) const
+bool YangSchema::isList(const schemaPath_& location, const ModuleNodePair& node) const
 {
     const auto schemaNode = getSchemaNode(location, node);
     return schemaNode && schemaNode->nodetype() == LYS_LIST;
 }
 
-bool YangSchema::isPresenceContainer(const path_& location, const ModuleNodePair& node) const
+bool YangSchema::isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const
 {
     if (!isContainer(location, node))
         return false;
     return libyang::Schema_Node_Container(getSchemaNode(location, node)).presence();
 }
 
-bool YangSchema::leafEnumHasValue(const path_& location, const ModuleNodePair& node, const std::string& value) const
+bool YangSchema::leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const
 {
     if (!isLeaf(location, node) || leafType(location, node) != yang::LeafDataTypes::Enum)
         return false;
@@ -128,7 +129,7 @@
     return std::any_of(enm.begin(), enm.end(), [=](const auto& x) { return x->name() == value; });
 }
 
-bool YangSchema::listHasKey(const path_& location, const ModuleNodePair& node, const std::string& key) const
+bool YangSchema::listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const
 {
     if (!isList(location, node))
         return false;
@@ -143,14 +144,14 @@
     return set->number() == 1;
 }
 
-libyang::S_Set YangSchema::getNodeSet(const path_& location, const ModuleNodePair& node) const
+libyang::S_Set YangSchema::getNodeSet(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string absPath = location.m_nodes.empty() ? "" : "/";
     absPath += pathToAbsoluteSchemaString(location) + "/" + fullNodeName(location, node);
     return m_context->find_path(absPath.c_str());
 }
 
-libyang::S_Schema_Node YangSchema::getSchemaNode(const path_& location, const ModuleNodePair& node) const
+libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
 {
     const auto set = getNodeSet(location, node);
     if (!set)
@@ -161,7 +162,7 @@
     return *schemaSet.begin();
 }
 
-const std::set<std::string> YangSchema::listKeys(const path_& location, const ModuleNodePair& node) const
+const std::set<std::string> YangSchema::listKeys(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::set<std::string> keys;
     if (!isList(location, node))
@@ -174,7 +175,7 @@
     return keys;
 }
 
-yang::LeafDataTypes YangSchema::leafType(const path_& location, const ModuleNodePair& node) const
+yang::LeafDataTypes YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
 {
     using namespace std::string_literals;
     if (!isLeaf(location, node))
@@ -210,7 +211,7 @@
     return res;
 }
 
-std::set<std::string> YangSchema::childNodes(const path_& path, const Recursion recursion) const
+std::set<std::string> YangSchema::childNodes(const schemaPath_& path, const Recursion recursion) const
 {
     using namespace std::string_view_literals;
     std::set<std::string> res;
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index c61232f..e4d6d5d 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -29,17 +29,17 @@
     YangSchema();
     ~YangSchema() override;
 
-    bool isContainer(const path_& location, const ModuleNodePair& node) const override;
-    bool isLeaf(const path_& location, const ModuleNodePair& node) const override;
-    bool isModule(const path_& location, const std::string& name) const override;
-    bool isList(const path_& location, const ModuleNodePair& node) const override;
-    bool isPresenceContainer(const path_& location, const ModuleNodePair& node) const override;
-    bool leafEnumHasValue(const path_& location, const ModuleNodePair& node, const std::string& value) const override;
-    bool listHasKey(const path_& location, const ModuleNodePair& node, const std::string& key) const override;
+    bool isContainer(const schemaPath_& location, const ModuleNodePair& node) const override;
+    bool isLeaf(const schemaPath_& location, const ModuleNodePair& node) const override;
+    bool isModule(const schemaPath_& location, const std::string& name) const override;
+    bool isList(const schemaPath_& location, const ModuleNodePair& node) const override;
+    bool isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const override;
+    bool leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const override;
+    bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const override;
     bool nodeExists(const std::string& location, const std::string& node) const override;
-    const std::set<std::string> listKeys(const path_& location, const ModuleNodePair& node) const override;
-    yang::LeafDataTypes leafType(const path_& location, const ModuleNodePair& node) const override;
-    std::set<std::string> childNodes(const path_& path, const Recursion recursion) const override;
+    const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
+    yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
+    std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const override;
 
     void registerModuleCallback(const std::function<std::string(const char*, const char*, const char*)>& clb);
 
@@ -57,12 +57,12 @@
 
 private:
     std::set<std::string> modules() const;
-    bool nodeExists(const path_& location, const ModuleNodePair& node) const;
+    bool nodeExists(const schemaPath_& location, const ModuleNodePair& node) const;
 
     /** @short Returns a set of nodes, that match the location and name criteria. */
-    std::shared_ptr<libyang::Set> getNodeSet(const path_& location, const ModuleNodePair& node) const;
+    std::shared_ptr<libyang::Set> getNodeSet(const schemaPath_& location, const ModuleNodePair& node) const;
 
     /** @short Returns a single Schema_Node if the criteria matches only one, otherwise nullptr. */
-    std::shared_ptr<libyang::Schema_Node> getSchemaNode(const path_& location, const ModuleNodePair& node) const;
+    std::shared_ptr<libyang::Schema_Node> getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const;
     std::shared_ptr<libyang::Context> m_context;
 };