1

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.

Alexander
  • 779
  • 8
  • 17
  • To be honest, I find the whole `node_type` class dodgy, it seems to reimplement what `boost::variant` does better – or am I misunderstanding its purpose? – Konrad Rudolph Mar 29 '14 at 17:21
  • Every node has to hold type id AND value of this type. So boost::variant is not the same. – Alexander Mar 29 '14 at 17:36
  • 1
    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 Mar 29 '14 at 17:38
  • 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 Mar 29 '14 at 17:44
  • 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::variant`s. It sounds like you simply need to create different classes for all your possible types, and fill the variant appropriately. – Konrad Rudolph Mar 29 '14 at 17:46
  • No its not. Anyway i'm not asking what data structure i should use. I'm asking how to parse using given struct. I'm sure its quite possible with spirit. – Alexander Mar 29 '14 at 17:49
  • Did you at least **look** at the relevant section of the tutorial before summarily dismissing my advice? After all, that part of the tutorial will also show you, *in detail*, how to solve your problem. – Konrad Rudolph Mar 29 '14 at 17:51
  • I've read tutorial and there are no examples where one struct member is filled from data and other filled with arbitrary value. If you can give a link to such an example it would be great. – Alexander Mar 29 '14 at 17:54
  • See here: http://www.boost.org/doc/libs/1_55_0/libs/spirit/doc/html/spirit/qi/tutorials/mini_xml___asts_.html – Konrad Rudolph Mar 29 '14 at 17:59
  • Of course i've read it and tried it. – Alexander Mar 29 '14 at 18:35

1 Answers1

1

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"?

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633