boostorg / spirit

Boost.org spirit module
http://boost.org/libs/spirit
393 stars 161 forks source link

X3: Sequence operator doesn't collapse when attribute is a single element tuple #408

Closed syyyr closed 6 years ago

syyyr commented 6 years ago

Hello, I'm trying to make a rule, that parses integers separated by a slash, and the last one is separated by an asterisk:

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
namespace x3 = boost::spirit::x3;

struct ints_ {
    std::vector<int> m_vector;
};
BOOST_FUSION_ADAPT_STRUCT(ints_, m_vector)

struct ints_class;
x3::rule<ints_class, ints_> const ints = "ints";
auto const ints_def = x3::int_ % '/' >> '*' >> x3::int_;
BOOST_SPIRIT_DEFINE(ints)

int main(int argc, char* argv[])
{
    ints_ foo;
    std::string to_parse = "3432/432/4*4343";
    auto to_parse_iter = to_parse.begin();
    x3::parse(to_parse_iter, to_parse.end(), ints, foo);
}

Unfortunately, the code doesn't compile with this error:

/usr/include/boost/spirit/home/x3/operator/detail/sequence.hpp:150:25: error: static assertion failed: Size of the passed attribute is less than expected.
             actual_size >= expected_size

According to the Compound Attribute Rules section in the documentation, the attribute of the ints rule should be vector<int>:

a: A, b: B --> (a % b): vector<A>
a: vector<A>, b: A --> (a >> b): vector<A>

If I add another int field to the struct, it successfully compiles, but I would like to have all the integers in the same vector. Is there a solution to this? Am I not doing something right?

Kojoley commented 6 years ago

You want too much guessing from Spirit. It will not do collapsing because you want to parse sequence into tuple attribute (and size of the parser sequences do not match the size of the tuple attribute). To fix your code you need to add an intermediate rule which will do collapsing (and which result should be of std::vector type). But as a general rule I will suggest you to not use single element tuples (you can simply replace ints_ with std::vector<int> in your case, or with struct ints_ : std::vector<int> { }; if you need a "strong typedef").

If I add another int field to the struct, it successfully compiles

That extends your tuple type from tuple<vector<int>> to tuple<vector<int>, int> which is perfectly compatible with x3::int_ % '/' >> '*' >> x3::int_ parser.

It is not a bug in Spirit but a missing feature, and I do not think it is worth the complexity (refs https://github.com/boostorg/spirit/pull/178#issuecomment-198802189 and https://github.com/boostorg/spirit/issues/463).

syyyr commented 6 years ago

Okay, so the root problem lies in that I use single element structures?

Kojoley commented 6 years ago

Okay, so the root problem lies in that I use single element structures?

Yes, quickfix is to replace

struct ints_ {
    std::vector<int> m_vector;
};
BOOST_FUSION_ADAPT_STRUCT(ints_, m_vector)

with

struct ints_ : std::vector<int> {};

or even

typedef std::vector<int> ints_;
syyyr commented 6 years ago

Okay, thank you.