2

I'm trying to parse input which has either a plus or minus character, followed by an X or Y character, followed by an unsigned integer.

(char_('+') | char_('-')) >> char_("xyXY") >> uint_

According to my reading of the docs, the synthesised attribute for this would be tuple<vector<char>,unsigned int> because the alternative parser (char | char) would be of type char, the char >> char("xyXY") would be vector<char>, and the vector<char> >> uint_ would be a tuple of the types, so tuple<vector<char>,unsigned int>. This fails to compile

qi\detail\assign_to.hpp(152) : error C2440: 'static_cast' : cannot convert from 'const char' to 'boost::tuples::tuple<T0,T1>'

Code:

#include <iostream>
#include <string>
#include <vector>

#include <boost/fusion/include/tuple.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/tuple/tuple.hpp>

using namespace boost::spirit::qi;

int main()
{
    std::string input("-Y 512");
    typedef std::string::const_iterator Iterator;
    Iterator first = input.begin();
    Iterator last = input.end();
    boost::tuple<std::vector<char>,unsigned int> output;
    bool result = phrase_parse(first,last,(char_('+') | char_('-')) >> char_("xyXY") >> uint_,ascii::space,output);
    if(result && first == last)
        std::cout << "sign=" << boost::get<0>(output)[0] << ", xy=" << boost::get<0>(output)[1] << ", size=" << boost::get<1>(output) << '\n';
    else
        std::cerr << "Parse error\n";
}

I then tried tuple<char,char,unsigned int> as the attribute type:

#include <iostream>
#include <string>
#include <vector>

#include <boost/fusion/include/tuple.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/tuple/tuple.hpp>

using namespace boost::spirit::qi;

int main()
{
    std::string input("-Y 512");
    typedef std::string::const_iterator Iterator;
    Iterator first = input.begin();
    Iterator last = input.end();
    boost::tuple<char,char,unsigned int> output;
    bool result = phrase_parse(first,last,(char_('+') | char_('-')) >> char_("xyXY") >> uint_,ascii::space,output);
    if(result && first == last)
        std::cout << "sign=" << boost::get<0>(output) << ", xy=" << boost::get<1>(output) << ", size=" << boost::get<2>(output) << '\n';
    else
        std::cerr << "Parse error\n";
}

This compiles but the output is incorrect. The first token of the input is parsed correctly, but the subsequent tokens aren't:

sign=-, xy= , size=0

I also tried as_string[]:

#include <iostream>
#include <string>
#include <vector>

#include <boost/fusion/include/tuple.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/tuple/tuple.hpp>

using namespace boost::spirit::qi;

int main()
{
    std::string input("-Y 512");
    typedef std::string::const_iterator Iterator;
    Iterator first = input.begin();
    Iterator last = input.end();
    boost::tuple<std::string,unsigned int> output;
    bool result = phrase_parse(first,last,as_string[(char_('+') | char_('-')) >> char_("xyXY")] >> uint_,ascii::space,output);
    if(result && first == last)
        std::cout << "sign=" << boost::get<0>(output)[0] << ", xy=" << boost::get<0>(output)[1] << ", size=" << boost::get<1>(output) << '\n';
    else
        std::cerr << "Parse error\n";
}

This improved things as the x/y token got parsed, but not the third integer token:

sign=-, xy=Y, size=0

Please show me where I'm going wrong.

I'm using Spirit version 2.5.2 (from Boost 1.58.0) and Microsoft Visual Studio 2008.

PeteUK
  • 1,062
  • 12
  • 26

1 Answers1

3

Spirit library docs recommend to use Fusion tuple. I think I saw somewhere (can't find it now) that Boost tuple may not be fully compatible with Spirit library.

Here is your fixed example:

#include <iostream>
#include <string>
#include <vector>

#include <boost/fusion/include/tuple.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/sequence.hpp>

namespace qi = boost::spirit::qi;

int main()
{
    std::string input("-Y 512");
    typedef std::string::const_iterator Iterator;
    Iterator first = input.begin();
    Iterator last = input.end();

    boost::fusion::tuple<char, char, unsigned int> output;
    bool result = qi::phrase_parse(first, last, (qi::char_('+') | qi::char_('-')) >> qi::char_("xyXY") >> qi::uint_, qi::ascii::space, output);
    if (result && first == last)
        std::cout << "sign=" << boost::fusion::get<0>(output) << ", xy=" << boost::fusion::get<1>(output) << ", size=" << boost::fusion::get<2>(output) << '\n';
    else
        std::cerr << "Parse error\n";

    return 0;
}

Output: sign=-, xy=Y, size=512

Update: Actually I found here that it's possible to use boost::tuple but different header needs to be included: #include <boost/fusion/include/boost_tuple.hpp>.

Community
  • 1
  • 1
doqtor
  • 8,414
  • 2
  • 20
  • 36