Boost.Spirit X3替代操作员

时间:2018-05-21 21:52:23

标签: c++ boost-spirit boost-spirit-x3

我有以下代码:

#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的返回值表示解析失败。但是根据我对documentation的解读,它应该尝试第二种选择,如果它不能解析第一种选择。

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

修改:看到回复后,我意识到x3::parse实际上正在返回解析成功。我正在检查它是否解析了整个流first == last,以确定成功,如文档中所示。然而,这隐藏了这样一个事实,即由于klean star的贪婪本质并且没有锚定到流的末尾,它成功地能够解析输入的一部分。谢谢大家。

2 个答案:

答案 0 :(得分:2)

这里的问题是“3”是(x3::uint32 % '|')解析器的有效输入,因此备选方案的第一个分支通过,仅消耗3.

您解决此问题的最简洁方法是获取替代列表而非替代列表。

即:

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

但是,这意味着你必须解析不同的结构。

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

修改

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

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

如果第一个分支没有到达流的末尾,则会强制第一个分支失败,从而进入备选方案。

答案 1 :(得分:1)

正如弗兰克评论的那样,Kleene列表运算符的问题是贪婪的,接受尽可能多的元素匹配,并考虑到“匹配”。

如果您希望它在“某些元素尚未被解析”时拒绝输入,请将其设为:

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

演示

<强> ViewAnimationUtils

#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, }