Reputation: 737
im currently working on a Spirit based Expression Parser that should allow in the end (far far in the future) expressions like
"a*b*c"
"10+20*x.y.z"
"a.b.c[ a.b ][ e.c( d.e()*4 )].e.f( (a.b+23)*d, -23*b.e(a.b.c) ).x.y"
wild mix of member accesses, array subscriptions, function calling and expressions
[] -> subscription
() -> function call or expression bracket
. member chaining
currently im fighting with the space skipping on member-chaining
"a . b . c"
is not valid in my world - but gets parsed due to space skipping feature
try online my reduced sample: https://wandbox.org/permlink/o5kcYtUQEfKZqJgw
Problem is line 23:
qi::rule<std::string::iterator, qi::blank_type, utree()> identifier_chain
= identifier >> *('.' >> identifier);
i can't use an qi::lexeme around the rule, i'll get an not castable to Skipper compile error but it works if i copy my complete identifier rule into the identifier_chain rule
qi::rule<std::string::iterator, qi::blank_type, utree()> identifer_chain
= qi::lexeme[qi::ascii::alpha >> *(qi::ascii::alnum | '_')
>> *('.' >> qi::ascii::alpha >> *(qi::ascii::alnum | '_'))];
but that seems very redundant and i think that copying will get me into trouble in the future when the parser is growing
any idea how to use the lexeme or something else to keep my '.' connects free of whitespaces so that subscription ends and member-chaining are strongly connected
].a
a.b
that is the only place in my parser where space skipping is not wanted for the rest its perfect to reduce the parser code
thx for any help/hints
Upvotes: 1
Views: 102
Reputation: 392999
This is how the skippers work (see Boost spirit skipper issues)
Your rule declares the skipper:
qi::rule<std::string::iterator, qi::blank_type, utree()> identifier_chain;
So to inhibit it you could surround with a lexeme, but you might just as well just drop the skipper from the declaration. The same actually goes for the the identifier rule, since it is also completely wrapped in lexeme[]
.
Suggested minimal fix:
#include <iostream>
#include <iomanip>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_utree.hpp>
namespace qi = boost::spirit::qi;
using boost::spirit::utree;
int main() {
auto tests = std::vector<std::string>{
"a", // ok
"a.b", // ok
" a.b ", // ok
"a . b", // error
"a . b. c" // error
};
for (std::string const str : tests) {
auto iter = str.begin(), end = str.end();
qi::rule<std::string::const_iterator, utree()>
identifier = qi::ascii::alpha >> *(qi::ascii::alnum | '_'),
identifier_chain = identifier >> *('.' >> identifier);
utree ut;
bool r = qi::phrase_parse(iter, end, identifier_chain >> qi::eoi, qi::blank, ut);
std::cout << std::quoted(str) << " ";
if (r) {
std::cout << "OK: " << ut << "\n";
} else {
std::cout << "Failed\n";
}
if (iter!=end) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(iter,end)) << "\n";
}
std::cout << "----\n";
}
return 0;
}
Prints:
"a" OK: ( "a" )
----
"a.b" OK: ( "a" "b" )
----
" a.b " OK: ( "a" "b" )
----
"a . b" Failed
Remaining unparsed: "a . b"
----
"a . b. c" Failed
Remaining unparsed: "a . b. c"
----
Upvotes: 2