It seems to me that the object
rule, being the starting point, must be declared as
qi::rule<It, QVariant(), Skipper> object;
Although I have no clue what QVariant is, I know this:
For attribute propagation to work, you need to have attribute type compatibility using the builtin Qi transformation heuristics.
For the first branch (self>>'.'>>identifier
) this /could/ be simple enough. Let's assume identifier
synthesizes a string-compatible attribute (std::string
or std::vector<char>
e.g.) then the resulting attribute could legally be assigned as a string.
The Sample
As a simple example, look at this (where I "emulate" something like what QVariant
could be):
Live On Coliru
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
using QVariant = boost::variant<std::string, int>;
template <typename Iterator, typename Skipper>
struct grammar : qi::grammar<Iterator, QVariant(), Skipper>
{
grammar() : grammar::base_type(object)
{
identifier = qi::raw[qi::lexeme[qi::alpha >> *(qi::alnum | '_' | ('-' >> qi::alnum))]];
self = (qi::raw[qi::lexeme["self"]]);
object =
qi::as_string [self >> '.' >> identifier]
//|qi::as_string [object >> '.' >> identifier] // there is no runtime error without that line
;
}
private:
qi::rule<Iterator, QVariant(), Skipper> object;
qi::rule<Iterator, std::string(), Skipper> identifier;
qi::rule<Iterator, std::string(), Skipper> self;
};
int main() {
using It = std::string::const_iterator;
std::string input = "self.foo.bar2.baz";
It f = input.begin(), l = input.end();
QVariant parsed;
bool ok = qi::phrase_parse(f, l, grammar<It, qi::space_type>{}, qi::space, parsed);
if (ok)
std::cout << "Parsed: " << parsed << "\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
Printing:
Parsed: selffoo
Remaining unparsed: '.bar2.baz'
The Problem
The second branch
qi::as_string [object >> '.' >> identifier]
would have to synthesize to tuple<QVariant, std::string>
to be consistent with the rest of the declarations. There is no way for Spirit to automatically transform that. The heuristic system might start grabbing at straws, and try to treat the bound attribute (remember, this is the enigmatic QVariant) as a container. If it succeeds at this¹ things would compile. Obviously, at runtime things come crashing down because the incorrect interfaces are invoked for the actual - runtime - value of the QVariant.
This is the theory.
A Solution?
Looking at the working demo, note that the '.'
is excluded. This leads me to suspect that you actually do not want any complicated chained "list" of object dereferences, but instead might just want to treat the whole matched input as a raw string? In that case, the simplest solution would be to lift the raw[]
a level, and perhaps use a string instead of QVariant
.
¹ e.g. because QVariant interface is slightly sloppy/unsafe and exposes .begin/.end/value_type/insert members directly on the variant interface?