1
votes

My AST node is struct

  struct node_type
  {
    type::code type_id; 
    boost::variant<int, std::string> value;
  };

Adapter and grammar:

BOOST_FUSION_ADAPT_STRUCT(
    client::node_type,
    (client::type::code, id)
    (boost::variant<int, std::string>, value)
)

namespace client
{  
  struct or_op
  {
    node_type left, right;
  };
  namespace type
  {
    enum code{NUMBER, STRING, BOOL};
  }      
  // Grammar
  template <typename Iterator>
  struct pair_grammar : qi::grammar<
    Iterator,
    node_type(), // Grammar generates node_type
    ascii::space_type
  >
  {
    pair_grammar() : pair_grammar::base_type(
        expr // main 'rule'
      )
    {
      using qi::lit;
      using qi::lexeme;
      using ascii::char_;
      using qi::int_;
      using ascii::string;
      using namespace qi::labels;

      using phoenix::at_c;
      using phoenix::push_back;

      expr = int_[at_c<0>(qi::_val) = 0, at_c<1>(qi::_val) = qi::_1];
    }
    qi::rule<Iterator, node_type(), ascii::space_type> expr;
  };
}

The code above doesn't compile. First of all i get warning

error: macro "BOOST_FUSION_ADAPT_STRUCT_FILLER_0" passed 3 arguments, but takes just 2

and then a lot of errors, starting with

qi/nonterminal/rule.hpp:303:17: error: no match for call to '(const function_type {aka const boost::function<bool(const char*&, const char* const&, boost::spirit::context<boost::fusion::cons<client::node_type&, boost::fusion::nil>, boost::fusion::vector0<> >&, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >&)>}) (const char*&, const char* const&, boost::spirit::qi::rule<const char*, client::node_type(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>::context_type&, const boost::spirit::unused_type&)'

What am I doing wrong? Thanks.

EDIT: Macro warning appeared because of ',' in macro. Typedef solved problem

  typedef boost::variant<int, std::string> node_value_t;
  struct node_type
  {
    type::code type_id; 
    node_value_t value;
  };

BOOST_FUSION_ADAPT_STRUCT(
    client::node_type,
    (client::type::code, type_id)
    (client::node_value_t, value)
)

But code still doesn't compile. I also tried

number = int_[qi::_val = phoenix::construct<node_type>(type::NUMBER, qi::_1)];

But that didnt help.

EDIT 2: Simplified original rule. Still no success.

1
To be honest, I find the whole node_type class dodgy, it seems to reimplement what boost::variant<int, bool, std::string> does better – or am I misunderstanding its purpose? - Konrad Rudolph
Every node has to hold type id AND value of this type. So boost::variant<int, bool, std::string> is not the same. - Alexander
Actually it is, that’s the whole point of boost::variant: it does hold its value’s type ID. It needs to, to be able to dispatch them. So yes: use boost::variant instead of your class. - Konrad Rudolph
This is simplified example. In real project there are many more types in node value such as functions, operators and so on. And type_id is return type of these operations. This type id in some cases even not known in parse stage and has to be filled in traversal stage. So the data structure is right. Question is how to fill it correctly. - Alexander
That is still a case for boost::variant. In fact, that’s precisely what it excels at. If you look at the Boost.Spirit examples you’ll see that they build complete parse trees with boost::variants. It sounds like you simply need to create different classes for all your possible types, and fill the variant appropriately. - Konrad Rudolph

1 Answers

1
votes

A number of issues here.

  1. The message

    error: macro "BOOST_FUSION_ADAPT_STRUCT_FILLER_0" passed 3 arguments, but takes just 2

    occurs because of the , in the template arguments for variant<>. You can fix it using a typedef:

    namespace client {
        namespace type { enum code{NUMBER, STRING, BOOL}; }      
    
        struct node_type {
            type::code type_id; 
            typedef boost::variant<int, std::string> vt_type;
            vt_type value;
        };
    }
    
    BOOST_FUSION_ADAPT_STRUCT(
            client::node_type,
            (client::type::code, type_id)
            (client::node_type::vt_type, value)
            )
    
  2. You were assigning an int to the attribute of enum type. The implicit conversion is not allowed. Instead, supply the required enum type:

    expr = int_ [at_c<0>(qi::_val) = client::type::NUMBER, at_c<1>(qi::_val) = qi::_1];
    

At this point, everything compiles and works: Live On Coliru

using namespace client;
pair_grammar<std::string::const_iterator> grammar;

std::string const input = "123";
auto f(input.begin()), l(input.end());

node_type node;
bool ok = qi::phrase_parse(f, l, grammar, ascii::space, node);
assert(ok);
assert(f == l);
assert(node.type_id == type::NUMBER);
assert(node.value == node_type::vt_type(123));

I don't think this solution is optimal, though.

Consider using the Spirit directives where possible, and staying away from Semantic actions that uglify the grammar, are a frequent source of errors/UB, make compile times even longer... [1]:

    pair_grammar() : pair_grammar::base_type(expr)
    {
        expr = qi::attr(client::type::NUMBER) >> qi::int_;
    }

See that Live On Coliru too:

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;
namespace ascii = qi::ascii;

namespace client
{
    namespace type
    {
        enum code{NUMBER, STRING, BOOL};
    }      

    struct node_type
    {
        type::code type_id; 
        typedef boost::variant<int, std::string> vt_type;
        vt_type value;
    };
}

/*Adapter and grammar:*/
BOOST_FUSION_ADAPT_STRUCT(
        client::node_type,
        (client::type::code, type_id)
        (client::node_type::vt_type, value)
        )

namespace client
{  
    struct or_op
    {
        node_type left, right;
    };
    // Grammar
    template <typename Iterator>
        struct pair_grammar : qi::grammar<
                              Iterator,
                              node_type(), // Grammar generates node_type
                              ascii::space_type
                              >
    {
        pair_grammar() : pair_grammar::base_type(expr)
        {
            expr = qi::attr(client::type::NUMBER) >> qi::int_;
        }
        qi::rule<Iterator, node_type(), ascii::space_type> expr;
    };
}

int main()
{
    using namespace client;
    pair_grammar<std::string::const_iterator> grammar;

    std::string const input = "123";
    auto f(input.begin()), l(input.end());

    node_type node;
    bool ok = qi::phrase_parse(f, l, grammar, ascii::space, node);
    assert(ok);
    assert(f == l);
    assert(node.type_id == type::NUMBER);
    assert(node.value == node_type::vt_type(123));
}

[1] Boost Spirit: "Semantic actions are evil"?