X3 的可变因子重复指令的改进
Improvements in repeat directive with variable factor for X3
我需要解析一个元素序列,其中第一个数字告诉接下来必须解析多少个元素。
作为我需要的简化:[3 10 20 30] 应该如下所示进行解析:
- Number of elements: 3
- Vector of elements: {10, 20, 30}
习惯了 Qi 及其 "repeat directive + phoenix" 语法,我在 X3 上尝试了相同的语法,但编译失败。我一直在网上寻找同样的问题,我在堆栈溢出中找到下一个 link:
我很震惊,在 Qi 中如此优雅地解决的问题在 X3 中怎么会如此丑陋和麻烦(个人意见,请不要冒犯)。当然,我得到了关于 phoonix 由于 c++14 替换而被放弃的原因。
但我想知道 X3 关于这个问题是否有任何进一步的改进,因为这个 post 是从 2015 年开始的。我一直在寻找,但没有找到。有什么建议吗?
注意 - 未包含代码,因为 case/code 与 posted link 相同 case/code。
谢谢。
通常这意味着该功能没有 PR(或者它有但有一些问题)。 repeat
也有设计问题。例如,您可以用它解析 {10 20 30}
,但不能解析 {10, 20, 30}
(需要一种 list
解析器)。
我不同意 Qi 有一种优雅的方式来做到这一点,因为您必须使用带有局部变量的规则或传递对外部值的引用。自然的方式似乎是 repeat(len_parser)[item_parser]
,但它有额外的船长设计问题(或者船长有限制复杂指令灵活性的设计问题)。
幸运的是,Spirit X3 在编写自己的解析器组合器方面要简单得多。
#include <boost/spirit/home/x3.hpp>
namespace x3e {
namespace x3 = boost::spirit::x3;
template <typename LenParser, typename Subject>
struct vlrepeat_directive : x3::unary_parser<Subject, vlrepeat_directive<LenParser, Subject>>
{
using base_type = x3::unary_parser<Subject, vlrepeat_directive<LenParser, Subject>>;
static bool const handles_container = true;
vlrepeat_directive(LenParser const& lp_, Subject const& subject)
: base_type(subject), lp(lp_) {}
template<typename Iterator, typename Context, typename RContext, typename Attribute>
bool parse(Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr) const
{
static_assert(x3::traits::has_attribute<LenParser, Context>::value, "must syntesize an attribute");
Iterator iter = first;
typename x3::traits::attribute_of<LenParser, Context>::type len;
if (!lp.parse(iter, last, context, rcontext, len))
return false;
for (; len; --len) {
if (!x3::detail::parse_into_container(
this->subject, iter, last, context, rcontext, attr))
return false;
}
first = iter;
return true;
}
LenParser lp;
};
template <typename LenParser>
struct vlrepeat_gen
{
template <typename Subject>
vlrepeat_directive<LenParser, typename x3::extension::as_parser<Subject>::value_type>
operator[](Subject const& p) const
{
return { lp, x3::as_parser(p) };
}
LenParser lp;
};
template <typename Parser>
vlrepeat_gen<Parser> vlrepeat(Parser const& p)
{
static_assert(x3::traits::is_parser<Parser>::value, "have to be a parser");
return { p };
}
}
template <typename LenParser, typename Subject, typename Context>
struct boost::spirit::x3::traits::attribute_of<x3e::vlrepeat_directive<LenParser, Subject>, Context>
: build_container<typename attribute_of<Subject, Context>::type> {};
并使用它:
#include <iostream>
#include <vector>
int main()
{
namespace x3 = boost::spirit::x3;
auto s = "5: 1 2 3 4 5", e = s + std::strlen(s);
std::vector<int> v;
if (phrase_parse(s, e, x3e::vlrepeat(x3::uint_ >> ':')[x3::int_], x3::space, v)) {
std::cout << "Result:\n";
for (auto x : v)
std::cout << x << '\n';
}
else
std::cout << "Failed!\n";
}
输出:
Result:
1
2
3
4
5
https://wandbox.org/permlink/K572K0BMEqA8lMJm
(它调用了 detail::parse_into_container
而不是 public API)
我需要解析一个元素序列,其中第一个数字告诉接下来必须解析多少个元素。
作为我需要的简化:[3 10 20 30] 应该如下所示进行解析:
- Number of elements: 3
- Vector of elements: {10, 20, 30}
习惯了 Qi 及其 "repeat directive + phoenix" 语法,我在 X3 上尝试了相同的语法,但编译失败。我一直在网上寻找同样的问题,我在堆栈溢出中找到下一个 link:
我很震惊,在 Qi 中如此优雅地解决的问题在 X3 中怎么会如此丑陋和麻烦(个人意见,请不要冒犯)。当然,我得到了关于 phoonix 由于 c++14 替换而被放弃的原因。
但我想知道 X3 关于这个问题是否有任何进一步的改进,因为这个 post 是从 2015 年开始的。我一直在寻找,但没有找到。有什么建议吗?
注意 - 未包含代码,因为 case/code 与 posted link 相同 case/code。
谢谢。
通常这意味着该功能没有 PR(或者它有但有一些问题)。 repeat
也有设计问题。例如,您可以用它解析 {10 20 30}
,但不能解析 {10, 20, 30}
(需要一种 list
解析器)。
我不同意 Qi 有一种优雅的方式来做到这一点,因为您必须使用带有局部变量的规则或传递对外部值的引用。自然的方式似乎是 repeat(len_parser)[item_parser]
,但它有额外的船长设计问题(或者船长有限制复杂指令灵活性的设计问题)。
幸运的是,Spirit X3 在编写自己的解析器组合器方面要简单得多。
#include <boost/spirit/home/x3.hpp>
namespace x3e {
namespace x3 = boost::spirit::x3;
template <typename LenParser, typename Subject>
struct vlrepeat_directive : x3::unary_parser<Subject, vlrepeat_directive<LenParser, Subject>>
{
using base_type = x3::unary_parser<Subject, vlrepeat_directive<LenParser, Subject>>;
static bool const handles_container = true;
vlrepeat_directive(LenParser const& lp_, Subject const& subject)
: base_type(subject), lp(lp_) {}
template<typename Iterator, typename Context, typename RContext, typename Attribute>
bool parse(Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr) const
{
static_assert(x3::traits::has_attribute<LenParser, Context>::value, "must syntesize an attribute");
Iterator iter = first;
typename x3::traits::attribute_of<LenParser, Context>::type len;
if (!lp.parse(iter, last, context, rcontext, len))
return false;
for (; len; --len) {
if (!x3::detail::parse_into_container(
this->subject, iter, last, context, rcontext, attr))
return false;
}
first = iter;
return true;
}
LenParser lp;
};
template <typename LenParser>
struct vlrepeat_gen
{
template <typename Subject>
vlrepeat_directive<LenParser, typename x3::extension::as_parser<Subject>::value_type>
operator[](Subject const& p) const
{
return { lp, x3::as_parser(p) };
}
LenParser lp;
};
template <typename Parser>
vlrepeat_gen<Parser> vlrepeat(Parser const& p)
{
static_assert(x3::traits::is_parser<Parser>::value, "have to be a parser");
return { p };
}
}
template <typename LenParser, typename Subject, typename Context>
struct boost::spirit::x3::traits::attribute_of<x3e::vlrepeat_directive<LenParser, Subject>, Context>
: build_container<typename attribute_of<Subject, Context>::type> {};
并使用它:
#include <iostream>
#include <vector>
int main()
{
namespace x3 = boost::spirit::x3;
auto s = "5: 1 2 3 4 5", e = s + std::strlen(s);
std::vector<int> v;
if (phrase_parse(s, e, x3e::vlrepeat(x3::uint_ >> ':')[x3::int_], x3::space, v)) {
std::cout << "Result:\n";
for (auto x : v)
std::cout << x << '\n';
}
else
std::cout << "Failed!\n";
}
输出:
Result:
1
2
3
4
5
https://wandbox.org/permlink/K572K0BMEqA8lMJm
(它调用了 detail::parse_into_container
而不是 public API)