Boost.Spirit X3 备选运算符

Boost.Spirit X3 Alternative Operator

我有以下代码:

#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 的 return 值表示解析 失败 。但根据我对 documentation 的阅读,如果它不能用第一个解析,它应该尝试第二个替代方案。

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

编辑:看到回复后,我意识到x3::parse实际上是return解析成功.我正在检查它是否已解析整个流 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 列表运算符的问题是贪婪的,接受尽可能多的匹配元素,并考虑到 "match".

如果你想让它在 "some elements have not been parsed" 时拒绝输入,就这样:

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

演示

Live On Coliru

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