2
votes

Here's a sample code

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

#include <iostream>
#include <string>

namespace qi = boost::spirit::qi;

    template <typename Iterator>
    struct input : qi::grammar<Iterator, unsigned()>
    {
        input() : input::base_type(start)
        {
            using qi::lit;
            using qi::double_;

            start = lit("ADD") 
                            >> +(
                                    +lit(" ")
                                    >> double_ 
                                    >> +lit(" ")
                                    >> double_
                                ); 
        }

        qi::rule<Iterator, unsigned()> start;
    };

int main()
{

    input<std::string::const_iterator> input_parser; // Our grammar
    std::string str = "ADD 1132.324 2342.234";
    unsigned result;

    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end = str.end();

    bool r = qi::parse(iter, end, input_parser, result);

}

I get the following error.

/usr/local/include/boost/spirit/home/qi/nonterminal/rule.hpp:201: instantiated from ‘boost::spirit::qi::rule& boost::spirit::qi::rule::operator=(const Expr&) [with Expr = boost::proto::exprns_::expr&, const boost::proto::exprns_::expr&>, 1l>&, const boost::spirit::terminal&>, 2l>&, const boost::proto::exprns_::expr&>, 1l>&>, 2l>&, const boost::spirit::terminal&>, 2l>&>, 1l>&>, 2l>, Iterator = __gnu_cxx::__normal_iterator, std::allocator > >, T1 = unsigned int ()(), T2 = boost::fusion::unused_type, T3 = boost::fusion::unused_type, T4 = boost::fusion::unused_type]’ mini.c++:34: instantiated from ‘input::input() [with Iterator = __gnu_cxx::__normal_iterator, std::allocator > >]’ mini.c++:49: instantiated from here /usr/local/include/boost/spirit/home/qi/operator/plus.hpp:62: error: no type named ‘type’ in ‘struct boost::spirit::traits::container_valueâ€

The weird part is, if I use the minus (-) operation i.e.

start = lit("ADD") 
                            >> -(
                                    +lit(" ")
                                    >> double_ 
                                    >> +lit(" ")
                                    >> double_
                                );

... it compiles perfectly fine!

Compiling on gcc 4.3.4.

1

1 Answers

2
votes

The attribute exposed by your start rule is unsigned, while the plus-parser exposes a container type holding the attributes of the wrapped elements. Here are the attribute propagation rules from the docs:

a: A --> +a: vector<A>

(i.e. if the parser a exposes an attribute of type A, a parser +a will expose a (standard) container holding instances of A, for instance std::vector<A>).

In your case, the embedded parser exposes double's. For this reason, you need to change the code in order to work as expected:

template <typename Iterator>
struct input : qi::grammar<Iterator, std::vector<double>()>
{
    input() : input::base_type(start)
    {
        using qi::lit;
        using qi::double_;
        start = lit("ADD") >> +(+lit(" ") >> double_ >> +lit(" ") >> double_);
    }

    qi::rule<Iterator, std::vector<double>()> start;
};

int main() 
{
    input<std::string::const_iterator> input_parser; // Our grammar
    std::string str = "ADD 1132.324 2342.234";

    std::vector<double> result;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end = str.end();
    bool r = qi::parse(iter, end, input_parser, result);  
} 

But unfortunately, things are not as easy as it seems. The current version of Spirit has a bug preventing the code to work (even the soon to be released Boost V1.46 will still have this bug, but it's fixed in SVN trunk). The problem is, that plus does not 'flatten' the embedded elements into the provided container, resulting in every second double parsed by the code above to be lost.

The workaround is to avoid sequences exposing more than one attribute inside plus:

start = lit("ADD") >> +(+lit(" ") >> double_);

verifying afterwards that the number of elements parsed was even.

A side note: you seem to want to skip whitespace in between elements in the input. This can be achieved easier by using a skip parser:

template <typename Iterator>
struct input : qi::grammar<Iterator, std::vector<double>(), qi::space_type>
{
    input() : input::base_type(start)
    {
        using qi::lit;
        using qi::double_;
        start = lit("ADD") >> +double_;
    }

    qi::rule<Iterator, std::vector<double>(), qi::space_type> start;
};

int main() 
{
    input<std::string::const_iterator> input_parser; // Our grammar
    std::string str = "ADD 1132.324 2342.234";

    std::vector<double> result;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end = str.end();
    bool r = qi::phrase_parse(iter, end, input_parser, qi::space, result);  
} 

which at the same time circumvents the issue described above.