1

The following test case is a reduction of a larger, multi-file parser, hence the slightly odd order of declarations and definitions. It does not compile and my understanding is that the std::tuple trips it. From the docs it seems to me that the synthesized attribute there should be an std::tuple provided the correct includes are present. What am I doing wrong?

// -*- mode: c++; -*-

#include <iostream>
#include <tuple>

#include <boost/fusion/include/io.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/std_tuple.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>

#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;

#include <boost/spirit/include/support_istream_iterator.hpp>

namespace ast {

struct S {
    std::tuple< int, int > t;
    int i;
};

using boost::fusion::operator<<;

} // namespace ast

BOOST_FUSION_ADAPT_STRUCT (ast::S, t, i)

namespace parser {

using S_type = x3::rule< struct S_class, ast::S >;
BOOST_SPIRIT_DECLARE(S_type)

struct S_class;
const S_type S = "S";

using x3::int_;
const auto S_def = ( int_ >> int_ ) >> int_;

BOOST_SPIRIT_DEFINE(S)

struct S_class { };

} // namespace parser

template< typename Iterator >
bool parse (Iterator& iter, Iterator last, ast::S& s) {
    using x3::ascii::space;

#if 1
    return x3::phrase_parse (iter, last, parser::S, space, s);
#else
    return x3::parse (iter, last, ( x3::int_ >> x3::int_ ) >> x3::int_, s);
#endif // 0
}

int main () {
    const std::string s = "1 2 3";
    auto iter = s.begin ();

    ast::S obj;
    return !parse (iter, s.end (), obj);
}

Thanks a bunch.

Engineerist
  • 367
  • 2
  • 13
  • 1
    It seems that replacing the parenthesized int pair with a nonterminal that synthesizes a tuple fixes it. Or explicitly synthesizing an std::tuple via an `as<...>` directive as shown in another post. But it's still unclear why should it not work as is. – Engineerist Oct 27 '19 at 16:11

1 Answers1

1

It's a nested structure. However you parse into a flat synthesized tuple that doesn't match the AST structure:

In other words ((int, int), int) is not structurally compatible with (int, (int, int)) or even (int, int, int) as your rule would parse.

The mentioned workarounds help with the attribute coercion to reflect the desired (and required) structure.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    I thought that parenthesizing a sequence automatically synthesizes a `tuple`. I.e., `a >> b >> c` is a flat `tuple< int, int, int >`, but `(a >> b) >> c` is a `tuple< tuple< int, int >, int >`. So, you're saying that is not the case, regarding of the parenthesization, in the absence of semantic actions, the synthesized attribute is a flat `tuple`. – Engineerist Oct 28 '19 at 17:32
  • Yes. In the old days I knew how to easily inspect the synthesized attribute type (see https://stackoverflow.com/questions/9404189/detecting-the-parameter-types-in-a-spirit-semantic-action/9405265#9405265 or a more modernized take here https://stackoverflow.com/questions/58353782/attributes-of-sequence-and-list-operator-in-boost-spirit-qi/58358181#58358181). I _think_ in X# there should be something like a`attribute_of::type` trait but I haven't figured it out before. – sehe Oct 28 '19 at 17:41