I think I am having an issue understanding how my boost::spirit::qi
parser is supposed to be written. I simply want to pass matched substrings to functions via semantic actions. In an attempt to roughly emulate this boost tutorial, I have come up with the following code:
template<class Iterator>
struct _Calculator : public qi::grammar<Iterator> {
qi::rule<Iterator> number, result; //and a whole bunch of other ones too
qi::rule<Iterator,std::string()> variable;
Code& code;
Variables& variables;
_Calculator(Code& code_, Variables& variables_)
: _Calculator::base_type(result),
code(code_),
variables(variables_)
{
number =
lexeme[(qi::char_("1-9") >> +qi::char_("0-9"))[boost::phoenix::bind(&fPushIntCV, qi::_1, boost::ref(code), boost::ref(variables))]]
| lexeme[("0x" >> +qi::char_("0-9a-fA-F")) [boost::phoenix::bind(&fPushIntCV, qi::_1, boost::ref(code), boost::ref(variables))]]
| lexeme[("0b" >> +qi::char_("0-1")) [boost::phoenix::bind(&fPushIntCV, qi::_1, boost::ref(code), boost::ref(variables))]]
| lexeme[("0" >> +qi::char_("0-7")) [boost::phoenix::bind(&fPushIntCV, qi::_1, boost::ref(code), boost::ref(variables))]]
//some other junk
}
};
typedef _Calculator<std::string::const_iterator> Calculator;
When the declaration of fPushIntCV
looks like this:
void fPushIntCV (vector<char> my_str, Code& c, Variables& v);
I get this error:
function_ptr.hpp:89: error: conversion from 'char' to non-scalar type 'std::vector<char, std::allocator<char> >' requested
When I change the declaration of fPushIntCV
to look like this:
void fPushIntCV (char my_str, Code& c, Variables& v);
I get this error:
function_ptr.hpp:89: error: cannot convert 'std::vector<char, std::allocator<char> >' to 'char' in argument passing
I figured that the attribute of qi::_1
is changing, but I get unresolved references if I include both function prototypes and now pass boost::phoenix::bind
some ambiguous overloaded function pointer:
error: no matching function for call to 'bind(<unresolved overloaded function type>, const boost::spirit::_1_type&, ...
(my ...
for trailing unrelated garbage)
I know this is probably a really simple error and a really simple fix, but I'm having a helluva time understanding the spell to make the boost magic do its thing. What is the function prototype that this semantic action is expecting?
A few observations:
You don't seem to be using a skipper, so using lexeme
is redundant (see Boost spirit skipper issues)
You want to know how to detect the type of the attribute exposed by a parser expression: see Detecting the parameter types in a Spirit semantic action
The types are documented with the parser directives, though, so e.g. as_string[(qi::char_("1-9") >> +qi::char_("0-9"))]
results in boost::fusion::vector2<char, std::vector<char> >
, which is directly reflected in the error message on GCC:
boost/phoenix/bind/detail/preprocessed/function_ptr_10.hpp|50 col 39| error: could not convert ‘a0’ from ‘boost::fusion::vector2<char, std::vector<char> >’ to ‘std::vector<char>’
Prefer not to mix and match library placeholders/wrappers, e.g. boost::ref
and boost::phoenix::ref
You seem to be reinventing integer parsing; consider using qi::int_parser
instead
It seems that the case to parse 0
is missing :)
Assuming you want my_str
to simply reflect the input string including number base prefix, I could suggest using:
number =
as_string[(qi::char_("1-9") >> +qi::char_("0-9"))] [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
| as_string[("0x" >> +qi::char_("0-9a-fA-F")) ] [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
| as_string[("0b" >> +qi::char_("0-1")) ] [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
| as_string[("0" >> +qi::char_("0-7")) ] [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
//some other junk
;
However, this could be simplified to:
number = as_string[
(qi::char_("1-9") >> +qi::char_("0-9"))
| ("0x" >> +qi::char_("0-9a-fA-F"))
| ("0b" >> +qi::char_("01"))
| ("0" >> +qi::char_("0-7"))
] [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
;
Now, you would probably like to just parse an integer value instead:
number =
(
("0x" >> qi::int_parser<int, 16, 1>())
| ("0b" >> qi::int_parser<int, 2, 1>())
| ("0" >> qi::int_parser<int, 8, 1>())
| qi::int_ /* accepts "0" */) [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
;
Which handsomely does the conversions[1], and you can just take an int
:
void fPushIntCV (int my_number, Code& c, Variables& v) {
std::cout << "fPushIntCV: " << my_number << "\n";
}
[1] (there's also uint_parser
and you can parse long
, long long
etc.; even big integers like boost::multiprecision::cpp_int
should be no issue)
Here's a demo program using this, showing that the values are converted correctly (and: "0" is accepted :)): Live On Coliru
int main()
{
Code code;
Variables variables;
Calculator g(code, variables);
for (std::string const input : { "0", "0xef1A", "010", "0b10101" })
{
It f(input.begin()), l(input.end());
if(qi::parse(f, l, g))
std::cout << "Parse success ('" << input << "')\n";
else std::cout << "Parse failed ('" << input << "')\n";
if (f != l)
std::cout << "Input remaining: '" << std::string(f, l) << "'\n";
}
}
Prints
fPushIntCV: 0
Parse success ('0')
fPushIntCV: 61210
Parse success ('0xef1A')
fPushIntCV: 8
Parse success ('010')
fPushIntCV: 21
Parse success ('0b10101')