Bubaya
Bubaya

Reputation: 823

Boost spirit x3: Parse vector, but only conditionally take items into result

Consider strings of the form (int ":" int )*. I want to parse such a string into a vector of integers, in the following way: if the second value is odd, add the first to the result. Otherwise, do nothing for this pair.

I am trying around with

  auto pair_ = x3::rule<class pair_, int>() 
             = x3::int_ >> ":" >> x3::int_;
  auto vec   = x3::rule<class vec, std::vector<int>>() 
             = (*pair_[
                 ([](auto &c){
                     if(x3::_attr(c).second % 2) 
                         x3::_val(c).push_back(x3::_attr(c).first);
                 })
               ]);

which seems to be wrong.

Additional task: make : int optional, and default to 1. I tried

  auto pair_ = x3::rule<class pair_, int>() 
             = x3::int_ >> ((":" >> x3::int_) | x3::attr(1));

Example: 10 11:0 12:3 should become the vector [10, 12].

which also seems to be wrong.

How do I do it correctly?

Upvotes: 2

Views: 113

Answers (1)

sehe
sehe

Reputation: 393769

Your pair_ rule exposes only an int. Changing

auto pair_ 
    = x3::rule<class pair_, std::pair<int, int> >() 
    = x3::int_ >> ":" >> x3::int_;

Will work, although you have to include

#include <boost/fusion/adapted/std_pair.hpp>

directly or indirectly. The optional version also just works:

auto pair_ 
    = x3::rule<class pair_, std::pair<int, int> >() 
    = x3::int_ >> (":" >> x3::int_ | x3::attr(1));

Live On Coliru

#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/home/x3.hpp>
#include <fmt/ranges.h>
namespace x3 = boost::spirit::x3;

namespace Parsing {
    auto pair_ 
        = x3::rule<class pair_, std::pair<int, int> >() 
        = x3::int_ >> (":" >> x3::int_ | x3::attr(1));

    auto action = [](auto& ctx) {
        if (_attr(ctx).second % 2)
            _val(ctx).push_back(_attr(ctx).first);
    };
    auto vec 
        = x3::rule<class vec, std::vector<int>>() 
        = *pair_[action];
}

int main() {
    for (std::string_view input : {
            "",
            "12:4 1:1",
            "12:4 1:1 2:2 3:3 4:4 5:5",
            "12:4 1 2 3 4 5:5",
            }) {
        std::vector<int> v;
        if (bool ok = phrase_parse(begin(input), end(input), Parsing::vec, x3::space, v))
            fmt::print("{}\t'{}' -> {}\n", ok, input, v);
    }
}

Printing

true    '' -> []
true    '12:4 1:1' -> [1]
true    '12:4 1:1 2:2 3:3 4:4 5:5' -> [1, 3, 5]
true    '12:4 1 2 3 4 5:5' -> [1, 2, 3, 4, 5]

Upvotes: 2

Related Questions