I have an issue with a Boost Spirit Qi grammar that is emitting an undesired type, resulting in this compilation error:
error C2664: 'std::basic_string<_Elem,_Traits,_Ax> &std::basic_string<_Elem,_Traits,_Ax>::insert(unsigned int,const std::basic_string<_Elem,_Traits,_Ax> &)' : cannot convert parameter 1 from 'std::_String_iterator<_Elem,_Traits,_Alloc>' to 'unsigned int'
Here is the grammar that is causing the issue:
qi::rule<Iterator, qi::unused_type()> gr_newline;
// asmast::label() just contains an identifier struct that is properly emitted from gr_identifier
qi::rule<Iterator, asmast::label(), skipper<Iterator> > gr_label;
gr_newline = +( char_('\r')
|char_('\n')
);
This fails:
gr_label = gr_identifier
>> ':'
> gr_newline;
But the following all work:
// This parses OK
gr_label = gr_identifier
> gr_newline;
// This also parses OK
gr_label = gr_identifier
> ':'
> gr_newline;
// This also parses OK
// **Why does this work when parenthesized?**
gr_label = gr_identifier
>> (':'
> skip_grammar.gr_newline
);
// This also parses OK
gr_label = gr_identifier
>> omit[':'
> gr_newline];
I don't understand why removing the character literal or omit[]ing it "fixes" the problem, but I don't want the grammar to be cluttered with that.
According the the compound attribute rules for >> and > found here, and character parser attributes here, gr_label should only emit asmast::label
a: A, b: B --> (a >> b): tuple<A, B>
a: A, b: Unused --> (a >> b): A <---- This one here is the one that should match so far as I understand
a: Unused, b: B --> (a >> b): B
a: Unused, b: Unused --> (a >> b): Unused
Expression Attribute
c unused or if c is a Lazy Argument, the character type returned by invoking it.
However, somehow something is polluting the intended attribute, and resulting in the compiler error.
So my questions are where this grammar emits an undesired attribute, and how to get rid of it.
The problem seems to be because you mix and match >
and >>
here.
Though you correctly observe the documented attribute generation rules, what really appears to happen is that the Unused
side is more like fusion::vector1<qi::unused_type>
(instead of qi::unused_type
).
Side note:
This also explains "Why does this work when parenthesized?" -
you are altering the order of expression evaluation (overruling operator precedence)
and getting another type.
You can find out whether my hunch is correct using techniques like e.g. Detecting the parameter types in a Spirit semantic action.
So, the short answer is: this is how it works. Whether that is a bug, an omission in the documentation or just a feature is really not constructive to discuss on SO. I refer to the [spirit-general] mailing list (note they also have an active #IRC channel).
The slightly longer answer is: I think there is more to it that you are not showing.
Two things:
The error message quite clearly indicates that it is trying to insert
a string iterator* where a character (or convertible) was expected. I don't see a connection with any of the code you posted, though I can guess at
I can compile your "problematic" rule specimen just fine embedded in my "mock-up" grammar I built up guessing from the little bits you exposed in your previous question.
I think maybe you should try to post another question, while showing a Short Self Contained Correct Example that exhibits the problem you are really having.
Be sure to mention the compiler version and version of boost used.
#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace asmast
{
typedef std::string label;
}
template <typename It, typename Skipper = qi::blank_type>
struct parser : qi::grammar<It, Skipper>
{
parser() : parser::base_type(start)
{
using namespace qi;
start = lexeme["Func" >> !(alnum | '_')] > function;
function = gr_identifier
>> "{"
>> -(
gr_instruction
| gr_label
//| gr_vardecl
//| gr_paramdecl
) % eol
> "}";
gr_instruction_names.add("Mov", unused);
gr_instruction_names.add("Push", unused);
gr_instruction_names.add("Exit", unused);
gr_instruction = lexeme [ gr_instruction_names >> !(alnum|"_") ] > gr_operands;
gr_operands = -(gr_operand % ',');
gr_identifier = lexeme [ alpha >> *(alnum | '_') ];
gr_operand = gr_identifier | gr_string;
gr_string = lexeme [ '"' >> *("\"\"" | ~char_("\"")) >> '"' ];
gr_newline = +( char_('\r')
|char_('\n')
);
gr_label = gr_identifier >> ':' > gr_newline;
BOOST_SPIRIT_DEBUG_NODES((start)(function)(gr_instruction)(gr_operands)(gr_identifier)(gr_operand)(gr_string));
}
private:
qi::symbols<char, qi::unused_type> gr_instruction_names;
qi::rule<It, Skipper> start, function, gr_instruction, gr_operands, gr_operand, gr_string;
qi::rule<It, qi::unused_type()> gr_newline;
qi::rule<It, asmast::label(), Skipper> gr_label, gr_identifier;
};
int main()
{
typedef boost::spirit::istream_iterator It;
std::cin.unsetf(std::ios::skipws);
It f(std::cin), l;
parser<It, qi::blank_type> p;
try
{
bool ok = qi::phrase_parse(f,l,p,qi::blank);
if (ok) std::cout << "parse success\n";
else std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
return ok;
} catch(const qi::expectation_failure<It>& e)
{
std::string frag(e.first, e.last);
std::cerr << e.what() << "'" << frag << "'\n";
}
return false;
}