Boost.Spirit X3 替代操作员

Boost.Spirit X3 Alternative Operator

本文关键字:操作员 X3 Spirit Boost      更新时间:2023-10-16

我有以下代码:

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
struct printer {
template <typename int_type>
void operator()(std::vector<int_type> &vec) {
std::cout << "vec(" << sizeof(int_type) << "): { ";
for( auto const &elem : vec ){
std::cout << elem << ", ";
}
std::cout << "}n";
}
};
template <typename Iterator>
void parse_int_list(Iterator first, Iterator last) {
namespace x3 = boost::spirit::x3;
x3::variant<vector<uint32_t>, vector<uint64_t>> vecs;
x3::parse( first, last,
(x3::uint32 % '|') | (x3::uint64 % '|'), vecs );
boost::apply_visitor(printer{}, vecs);
}

我希望首先尝试将输入解析为 32 位 uint 向量,然后如果失败为 64 位 uint 向量。 如果列表中的第一个整数与足以容纳列表中任何其他内容的类型匹配,则此方法非常有用。即,

string ints32 = "1|2|3";
parse_int_list(being(ints32), end(ints32))
// prints vec(4): { 1, 2, 3, }
string ints64 = "10000000000|20000000000|30000000000";
parse_int_list(being(ints64), end(ints64))
// prints vec(8): { 10000000000, 20000000000, 30000000000, }

但是,当第一个数字是 32 位,后面的数字是 64 位时,它不起作用。

string ints_mixed = "1|20000000000|30000000000";
parse_int_list(being(ints_mixed), end(ints_mixed))
// prints vec(4): { 1, }

返回值x3::parse表示分析失败。 但是根据我对文档的阅读,如果它无法解析第一种选择,它应该尝试第二种选择。

关于我如何错误地阅读此内容以及替代解析器实际如何工作的指示?

编辑:看到响应后,我意识到x3::parse实际上返回了解析成功。 我正在检查它是否已经解析了整个流,first == last,以确定成功,如文档所示。 然而,这掩盖了一个事实,即由于klean star的贪婪性质并且不锚定到流的末端,它能够成功地解析部分输入。 谢谢大家。

这里的问题是"3"是(x3::uint32 % '|')解析器的有效输入,因此替代的第一个分支通过,仅使用3。

解决此问题的最干净方法是使用替代项列表而不是列表的替代项。

即:

(x3::uint32 | x3::uint64) % '|'

但是,这意味着您必须在不同的结构中进行解析。

vector<x3::variant<uint32_t,uint64_t>> vecs;

编辑

或者,如果您不打算将此解析器用作子解析器,则可以在每个分支中强制输入结束。

(x3::uint32 % '|' >> x3::eoi) | (x3::uint64 % '|' >> x3::eoi)

如果第一个分支没有到达流的末尾,这将强制它失败,从而掉入替代分支。

正如 Frank 评论的那样,Kleene 列表运算符的问题很贪婪,接受尽可能多的元素匹配,并认为这是一个"匹配"。

如果您希望它在"某些元素尚未解析"时拒绝输入,请这样做:

parse(first, last, x3::uint32 % '|' >> x3::eoi | x3::uint64 % '|' >> x3::eoi, vecs);

演示

住在科里鲁

#include <boost/spirit/home/x3.hpp>
#include <iostream>
struct printer {
template <typename int_type> void operator()(std::vector<int_type> &vec) const {
std::cout << "vec(" << sizeof(int_type) << "): { ";
for (auto const &elem : vec) {
std::cout << elem << ", ";
}
std::cout << "}n";
}
};
template <typename Iterator> void parse_int_list(Iterator first, Iterator last) {
namespace x3 = boost::spirit::x3;
boost::variant<std::vector<uint32_t>, std::vector<uint64_t> > vecs;
parse(first, last, x3::uint32 % '|' >> x3::eoi | x3::uint64 % '|' >> x3::eoi, vecs);
apply_visitor(printer{}, vecs);
}
int main() {
for (std::string const input : {
"1|2|3",
"4294967295",
"4294967296",
"4294967295|4294967296",
}) {
parse_int_list(input.begin(), input.end());
}
}

指纹

vec(4): { 1, 2, 3, }
vec(4): { 4294967295, }
vec(8): { 4294967296, }
vec(8): { 4294967295, 4294967296, }