I have a parser that parses into a boost::variant<int, double, std::string>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using namespace std;
typedef map<string, boost::variant<int, double, string> > namevalue;
typedef pair<string, boost::variant<int, double, string> > namevaluepair;
template <typename Iterator>
struct keys_and_values2
: grammar<Iterator, namevalue(), ascii::blank_type>
{
keys_and_values2()
: keys_and_values2::base_type(start)
{
start %= query >> qi::eoi;
query = +pair;
value = qi::int_ | qi::double_ | qi::lexeme[+(qi::char_ - qi::eol)] ;
pair = key >> qi::lit(':') >> value >> qi::eol;
key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
}
qi::rule<Iterator, namevalue(), ascii::blank_type> start;
qi::rule<Iterator, namevalue(), ascii::blank_type> query;
qi::rule<Iterator, namevaluepair(), ascii::blank_type> pair;
qi::rule<Iterator, std::string(), ascii::blank_type> key;
qi::rule<Iterator, boost::variant<int, double, string>(), ascii::blank_type> value;
};
the input to parse is here:
Sarai : 52.731199473801
Jamiya : Jelly Drop
Clara : -92.408605869885
Janelle : 132349223
Briley : -40.905352495602
This fails on the first line "Sarai". If I reverse the parsing of double and int like this:
value = qi::double_ | qi::int_ | qi::lexeme[+(qi::char_ - qi::eol)] ;
It works fine, but the integer value of "Janelle" is parsed as double. Following the BOOST FAQ and the BOOST documentation, I would like to use qi::hold[] like this:
value = qi::hold[qi::int_] | qi::hold[qi::double_] | qi::lexeme[+(qi::char_ - qi::eol)] ;
But with this, I get a message about missing function: swap()
. This is actually documented in the boost docs (note). But the explanation is very terse. I am unable to find the correct attribute type for this swap function. Can anybody help here?
Compiler message here:
E:\Boost\boost_1_58_0\boost/spirit/home/support/attributes.hpp(1036) : error C2784: 'void boost::spirit::swap(boost::spirit::multi_pass<T,Policies> &,boost::spirit::multi_pass<T,Policies> &)' : could not deduce template argument for 'boost::spirit::multi_pass<T,Policies> &' from 'int'
Update
The answer of sehe works with the data above. However If I change the input to the following:
Sarai : 52.731199473801
Jamiya : Jelly Drop
Clara : -92.408605869885
Rebekah : 240ad9beb53bbfafcd5
Janelle : 132349223
Cloe : 456ABCabvc
Briley : -40.905352495602
I'll have a problem with the "Rebekah" value. I also see that the order in which the values are evaluated is important. This time, the issue is not with the double
value anymore, it is between the int
and the string
. I would like to implement something like this: (policy?)
- everything that has only digits, an optional minus sign and contains a dot is a
double
- everything that has only digits and an optional minus sign is
int
- everything else is
std::string
Solution
Sometimes, it is more important to understand the question than the problem.
The issue was not the parser policy, it was my definition of the parsing rules (see above). Rule 3 "everything else" actually includes: "everything up to EOL". Therefore all three alternatives in the rule for value
have to match this:
value = strict_double >> qi::eol
| qi::int_ >> qi::eol
| qi::lexeme[+(qi::char_ - qi::eol)] >> qi::eol;
With this change, sehe's answer just works like a charm!