Merge "Support trailing slashes in paths"
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 0cb64f1..c596821 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -476,3 +476,5 @@
             parserContext.m_topLevelModulePresent = false;
     }
 };
+
+struct trailingSlash_class;
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index 2711a30..557b01f 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -87,6 +87,11 @@
     bool operator==(const dataNode_& b) const;
 };
 
+enum class TrailingSlash {
+    Present,
+    NonPresent
+};
+
 enum class Scope {
     Absolute,
     Relative
@@ -96,12 +101,14 @@
     bool operator==(const schemaPath_& b) const;
     Scope m_scope = Scope::Relative;
     std::vector<schemaNode_> m_nodes;
+    TrailingSlash m_trailingSlash = TrailingSlash::NonPresent;
 };
 
 struct dataPath_ {
     bool operator==(const dataPath_& b) const;
     Scope m_scope = Scope::Relative;
     std::vector<dataNode_> m_nodes;
+    TrailingSlash m_trailingSlash = TrailingSlash::NonPresent;
 };
 
 std::string nodeToSchemaString(decltype(dataPath_::m_nodes)::value_type node);
@@ -118,5 +125,5 @@
 BOOST_FUSION_ADAPT_STRUCT(module_, m_name)
 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)
+BOOST_FUSION_ADAPT_STRUCT(dataPath_, m_scope, m_nodes, m_trailingSlash)
+BOOST_FUSION_ADAPT_STRUCT(schemaPath_, m_scope, m_nodes, m_trailingSlash)
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 7caa45c..d0101fb 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -28,6 +28,7 @@
 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<trailingSlash_class, TrailingSlash> const trailingSlash = "trailingSlash";
 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";
@@ -119,10 +120,13 @@
 auto const absoluteStart_def =
         x3::omit['/'] >> x3::attr(Scope::Absolute);
 
+auto const trailingSlash_def =
+        x3::omit['/'] >> x3::attr(TrailingSlash::Present);
+
 // I have to insert an empty vector to the first alternative, otherwise they won't have the same attribute
 auto const dataPath_def =
-        absoluteStart >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::eoi |
-        -(absoluteStart) >> dataNode % '/';
+        absoluteStart >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
+        -(absoluteStart) >> dataNode % '/' >> -trailingSlash;
 
 auto const dataNodeList_def =
         -(module) >> list;
@@ -136,12 +140,12 @@
         initializeContext >> x3::attr(decltype(dataPath_::m_nodes)()) >> dataNodeList;
 
 auto const dataPathListEnd_def =
-        absoluteStart >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::eoi |
-        -(absoluteStart) >> dataNodesListEnd;
+        absoluteStart >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
+        -(absoluteStart) >> dataNodesListEnd >> -trailingSlash;
 
 auto const schemaPath_def =
-        absoluteStart >> x3::attr(decltype(schemaPath_::m_nodes)()) >> x3::eoi |
-        -(absoluteStart) >> schemaNode % '/';
+        absoluteStart >> x3::attr(decltype(schemaPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
+        -(absoluteStart) >> schemaNode % '/' >> -trailingSlash;
 
 auto const leafPath_def =
         dataPath;
@@ -244,6 +248,7 @@
 BOOST_SPIRIT_DEFINE(dataNodesListEnd)
 BOOST_SPIRIT_DEFINE(dataPathListEnd)
 BOOST_SPIRIT_DEFINE(absoluteStart)
+BOOST_SPIRIT_DEFINE(trailingSlash)
 BOOST_SPIRIT_DEFINE(module)
 BOOST_SPIRIT_DEFINE(leaf_data)
 BOOST_SPIRIT_DEFINE(leaf_data_enum)
diff --git a/tests/cd.cpp b/tests/cd.cpp
index 85ea794..71eb6bb 100644
--- a/tests/cd.cpp
+++ b/tests/cd.cpp
@@ -39,13 +39,29 @@
         {
             SECTION("example:a")
             {
-                input = "cd example:a";
+                SECTION("trailing slash")
+                {
+                    input = "cd example:a/";
+                    expected.m_path.m_trailingSlash = TrailingSlash::Present;
+                }
+                SECTION("no trailing slash")
+                {
+                    input = "cd example:a";
+                }
                 expected.m_path.m_nodes.push_back(dataNode_(module_{"example"}, container_("a")));
             }
 
             SECTION("second:a")
             {
-                input = "cd second:a";
+                SECTION("trailing slash")
+                {
+                    input = "cd second:a/";
+                    expected.m_path.m_trailingSlash = TrailingSlash::Present;
+                }
+                SECTION("no trailing slash")
+                {
+                    input = "cd second:a";
+                }
                 expected.m_path.m_nodes.push_back(dataNode_(module_{"second"}, container_("a")));
             }