views:

40

answers:

1

Hi,

I have tried various approaches to fixing this issue with maps and casts, splitting the parse into different sub-pieces, using std::vector directly and trying _r1 etc. but I seem to have failed to grasp something fundamental about the use of attributes.

I want to parse a line such as:

DEFMACRO macroname param1 param2 param3 ... paramN

and add macroname into a qi::symbols parser along with its list of params.

Matching on

lit("DEFMACRO") >> (+char_) >> predicate_

and putting into a defmacro struct works fine, but when I try to use the result or store it whole as the data element of a symbols parser I get errors of the form

cannot convert from 'const boost::phoenix::actor' to 'const client::defmacro'

  • but whatever I try I always fail to "convert from 'const boost::phoenix::actor' to" whatever data type I am trying to use (eg straight to std::vector or other variations in structs. Also tried variations on syntax but so far drawn a blank.

Code fragment is below, followed by compiler output for this variation on my problem.

Any explanation of my failure to grasp some important concept very welcome.

Using VC++ 2008 with Spirit 1.42.

Thanks Rick

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_int.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <string>
#include <vector>

namespace client
{
    namespace fusion = boost::fusion;
    namespace phoenix = boost::phoenix;
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

 typedef std::vector<std::string> predicate;

 struct defmacro
    {
  std::string name;  // identifier for macro
  predicate params;  // parameters for macro
    };

}

BOOST_FUSION_ADAPT_STRUCT(
    client::defmacro,
    (std::string, name)
 (client::predicate, params)
)

namespace client
{
    template <typename Iterator>
    struct awe_grammar
      : qi::grammar<Iterator, awe(), qi::locals<std::string>, ascii::space_type>
    {
        awe_grammar()
          : awe_grammar::base_type(x, "x")
        {
            using qi::lit;
            using qi::eol;
            using qi::int_;
            using ascii::char_;
            using namespace qi::labels;

            using phoenix::at_c;

   long line_no=1;
   qi::symbols<std::string,  defmacro> macros;

   eol_ = eol[ref(line_no)++];

   predicate_ %= *(+char_); 

   defmacro_line_ %= (lit("DEFMACRO") >> (+char_) >> predicate_ >> eol_);

   // ******** This line will not compile *************************
   defmacro_  = defmacro_line_[macros.add(at_c<0>(_1),_1)];
   // *************************************************************
        }

  qi::rule<Iterator, defmacro(), ascii::space_type> defmacro_line_;
  qi::rule<Iterator, void(), ascii::space_type> defmacro_;
        qi::rule<Iterator, predicate(), ascii::space_type> predicate_;

 };
}

2>v:\awe\parser\parser\spirit\spirit_eg.cpp(XXX) : error C2664: 'const boost::spirit::qi::symbols<Char,T>::adder &boost::spirit::qi::symbols<Char,T>::adder::operator ()<boost::phoenix::actor<Eval>>(const Str &,const T &) const' : cannot convert parameter 2 from 'const boost::phoenix::actor<Eval>' to 'const client::defmacro &'
2>        with
2>        [
2>            Char=std::string,
2>            T=client::defmacro,
2>            Eval=boost::phoenix::composite<boost::phoenix::at_eval<0>,boost::fusion::vector<boost::spirit::argument<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,
2>            Str=boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::at_eval<0>,boost::fusion::vector<boost::spirit::argument<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>>
2>        ]
2>        and
2>        [
2>            Eval=boost::spirit::argument<0>
2>        ]
2>        Reason: cannot convert from 'const boost::phoenix::actor<Eval>' to 'const client::defmacro'
2>        with
2>        [
2>            Eval=boost::spirit::argument<0>
2>        ]
2>        No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
2>        v:\awe\parser\parser\spirit\spirit_eg.cpp(351) : while compiling class template member function 'client::awe_grammar<Iterator>::awe_grammar(void)'
2>        with
2>        [
2>            Iterator=std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>
2>        ]
2>        v:\awe\parser\parser\spirit\spirit_eg.cpp(622) : see reference to class template instantiation 'client::awe_grammar<Iterator>' being compiled
2>        with
2>        [
2>            Iterator=std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>
2>        ]
A: 

You can add values to symbols outside of your grammar like this:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_int.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <string>
#include <vector>

namespace client
{
    namespace fusion = boost::fusion;
    namespace phoenix = boost::phoenix;
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    typedef std::vector<std::string> predicate;

    struct defmacro
    {
        std::string name;  // identifier for macro
        predicate params;  // parameters for macro
    };

    typedef std::vector<defmacro> awe;
}

BOOST_FUSION_ADAPT_STRUCT(
    client::defmacro,
    (std::string, name)
    (client::predicate, params)
)

namespace client
{
    template <typename Iterator>
    struct awe_grammar
        : qi::grammar<Iterator, awe()>
    {
        awe_grammar()
            : awe_grammar::base_type(start_)
        {
            using qi::lit;
            using qi::eol;
            using ascii::char_;
            using ascii::blank;
            using ascii::space;
            using namespace qi::labels;
            using phoenix::ref;
            using phoenix::at_c;

            line_no = 0;

            eol_ = eol[++ref(line_no)];

            identifier_ %= qi::lexeme[+(char_ - space)];

            predicate_ %= (identifier_ % blank) >> eol_;

            defmacro_line_ %=
                   lit("DEFMACRO ")
                >> identifier_ >> ' '
                >> predicate_
            ;

            start_ %= +defmacro_line_;
        }

        long line_no;

        qi::rule<Iterator, void()       > eol_;
        qi::rule<Iterator, defmacro()   > defmacro_line_;
        qi::rule<Iterator, awe()        > start_;
        qi::rule<Iterator, std::string()> identifier_;
        qi::rule<Iterator, predicate()  > predicate_;

    };
}

and

#include <cstdlib>
#include <iostream>
#include "awe_grammar.h"

template <typename P, typename T>
void test_parser_attr(
                      char const* input, P const& p, T& attr, bool full_match = true)
{
    using boost::spirit::qi::parse;

    char const* f(input);
    char const* l(f + strlen(f));
    if (parse(f, l, p, attr) && (!full_match || (f == l)))
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}

int main( /*int _argc, char * _argv[]*/ )
{
    typedef client::awe_grammar<char const *> my_grammar;
    my_grammar g;
    client::awe result;

    test_parser_attr(
        "DEFMACRO macroname param1 param2 param3\n"
        "DEFMACRO macro2 param1 param2\n",
        g,
        result,
        true
    );

    ////////////////////
    // adding
    ////////////////////
    boost::spirit::qi::symbols<char, client::defmacro> macros;
    for (size_t i = 0; i < result.size(); i++)
    {
        macros.add(result[i].name, result[i]);
    }

    return EXIT_SUCCESS;
}

After that you can do with your macros what you want (for example, pass it to another grammar).

GooRoo
Thank you GooRoo - in one sense that is the perfect pragmatic answer in that I can just pick that up and use it (thank you). In another sense, though, I have not been taught to fish in that I still do not understand what do I have to do to get the phoenix Eval actor available in the code as the type it is meant to be - is this just fundamentally impossible in Phoenix lazy-eval land or is there some way of getting this argument recognised as a struct/Boost object that I have missed.Most grateful for working example but still short of insight into what I am doing and where I am being thick.
Rick Loukes
I think the insight I am missing is that I may need to use phoenix::construct or similar somehow. Any further thoughts out there ?Thanks, Rick
Rick Loukes
One of the boost-spirit developers can help you maybe: http://stackoverflow.com/users/269943/hkaiser
GooRoo