Merge "Rework path parsing"
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 14d3451..ab9958a 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -172,34 +172,6 @@
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)
- {
- 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)
- {
- 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 discard_class;
struct ls_class;
diff --git a/src/grammars.hpp b/src/grammars.hpp
index ae7a980..74a6a17 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -47,7 +47,7 @@
} const ls_options;
auto const ls_def =
- ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> (dataPathListEnd | dataPath | schemaPath | (module >> "*")));
+ ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> (dataPathListEnd | anyPath | (module >> "*")));
auto const cd_def =
cd_::name >> space_separator > dataPath;
@@ -132,7 +132,7 @@
copy_::name > space_separator > copy_args;
auto const describe_def =
- describe_::name >> space_separator > (dataPathListEnd | dataPath | schemaPath);
+ describe_::name >> space_separator > (dataPathListEnd | anyPath);
auto const createCommandSuggestions_def =
x3::eps;
diff --git a/src/path_parser.hpp b/src/path_parser.hpp
index 19ab2a2..0334d12 100644
--- a/src/path_parser.hpp
+++ b/src/path_parser.hpp
@@ -14,8 +14,6 @@
namespace x3 = boost::spirit::x3;
-x3::rule<dataPath_class, dataPath_> const dataPath = "dataPath";
-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";
@@ -128,6 +126,53 @@
NodeParser<schemaNode_> schemaNode;
NodeParser<dataNode_> dataNode;
+using AnyPath = boost::variant<schemaPath_, dataPath_>;
+
+struct PathParser : x3::parser<PathParser> {
+ template <typename It, typename Ctx, typename RCtx, typename Attr>
+ bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, Attr& attr) const
+ {
+ initializePath.parse(begin, end, ctx, rctx, attr);
+ dataPath_ attrData;
+
+ // absoluteStart has to be separate from the dataPath parser,
+ // otherwise, if the "dataNode % '/'" parser fails, the begin iterator
+ // gets reverted to before the starting slash.
+ auto res = -absoluteStart.parse(begin, end, ctx, rctx, attrData.m_scope);
+ auto dataPath = x3::attr(attrData.m_scope) >> dataNode % '/' >> -trailingSlash;
+ res = dataPath.parse(begin, end, ctx, rctx, attrData);
+ attr = attrData;
+
+ if constexpr (std::is_same<Attr, AnyPath>()) {
+ auto pathEnd = x3::rule<class PathEnd>{"pathEnd"} = &space_separator | x3::eoi;
+ // If parsing failed, or if there's more input we try parsing schema nodes
+ if (!res || !pathEnd.parse(begin, end, ctx, rctx, x3::unused)) {
+ // If dataPath parsed some nodes, they will be saved in `attrData`. We have to keep these.
+ schemaPath_ attrSchema = dataPathToSchemaPath(attrData);
+ auto schemaPath = schemaNode % '/';
+ // The schemaPath parser continues where the dataPath parser ended.
+ res = schemaPath.parse(begin, end, ctx, rctx, attrSchema.m_nodes);
+ auto trailing = -trailingSlash >> pathEnd;
+ res = trailing.parse(begin, end, ctx, rctx, attrSchema.m_trailingSlash);
+ attr = attrSchema;
+ }
+ }
+ return res;
+ }
+} const pathParser;
+
+// Need to use these wrappers so that my PathParser class gets the proper
+// attribute. Otherwise, Spirit injects the attribute of the outer parser that
+// uses my PathParser.
+// Example grammar: anyPath | module.
+// The PathParser class would get a boost::variant as the attribute, but I
+// don't want to deal with that, so I use these wrappers to ensure the
+// attribute I want (and let Spirit deal with boost::variant). Also, the
+// attribute gets passed to PathParser::parse via a template argument, so the
+// class doesn't even to need to be a template. Convenient!
+auto const anyPath = x3::rule<class anyPath_class, AnyPath>{"anyPath"} = pathParser;
+auto const dataPath = x3::rule<class dataPath_class, dataPath_>{"dataPath"} = pathParser;
+
#if __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Woverloaded-shift-op-parentheses"
@@ -172,9 +217,6 @@
auto const createPathSuggestions_def =
x3::eps;
-// I have to insert an empty vector to the first alternative, otherwise they won't have the same attribute
-auto const dataPath_def = initializePath >> absoluteStart >> createPathSuggestions >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi | initializePath >> -(absoluteStart >> createPathSuggestions) >> dataNode % '/' >> (-(trailingSlash >> createPathSuggestions) >> -(completing >> rest) >> (&space_separator | x3::eoi));
-
auto const dataNodeList_def =
createPathSuggestions >> -(module) >> list;
@@ -188,8 +230,6 @@
auto const dataPathListEnd_def = initializePath >> absoluteStart >> createPathSuggestions >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi | initializePath >> -(absoluteStart >> createPathSuggestions) >> dataNodesListEnd >> (-(trailingSlash >> createPathSuggestions) >> -(completing >> rest) >> (&space_separator | x3::eoi));
-auto const schemaPath_def = initializePath >> absoluteStart >> createPathSuggestions >> x3::attr(decltype(schemaPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi | initializePath >> -(absoluteStart >> createPathSuggestions) >> schemaNode % '/' >> (-(trailingSlash >> createPathSuggestions) >> -(completing >> rest) >> (&space_separator | x3::eoi));
-
auto const leafPath_def =
dataPath;
@@ -218,8 +258,6 @@
BOOST_SPIRIT_DEFINE(leafPath)
BOOST_SPIRIT_DEFINE(presenceContainerPath)
BOOST_SPIRIT_DEFINE(listInstancePath)
-BOOST_SPIRIT_DEFINE(schemaPath)
-BOOST_SPIRIT_DEFINE(dataPath)
BOOST_SPIRIT_DEFINE(dataPathListEnd)
BOOST_SPIRIT_DEFINE(initializePath)
BOOST_SPIRIT_DEFINE(createKeySuggestions)