I'm looking for the simplest way to implement variadic function which takes list of boost::spirit::qi rules and expands the list into expression of format: rule1 | rule2 | rule3 |.... Let's assume that the rules synthesize no attribute. Your kind help is very much appreciated.
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
#include <boost/spirit/include/phoenix_operator.hpp>
namespace qi = boost::spirit::qi;
namespace ph = boost::phoenix;
namespace ascii = boost::spirit::ascii;
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::ascii::space;
using boost::spirit::iso8859_1::char_;
typedef qi::rule<std::string::const_iterator,ascii::space_type> mrule_t;
typedef qi::rule< std::string::const_iterator,std::string() > wrule_t;
//How to deduce expandBitwise() return type ?
template<typename T>
T expandBitwise(T& t)
{
return t.rule_;
}
template<typename T,typename ...Tail>
T expandBitwise(T& t,Tail& ...tail)
{
return t.rule_ | expandBitwise(tail...);
}
struct TStruct
{
mrule_t rule_;
template<typename T,typename R>
TStruct( T& rVar,const std::string&name, R& rule ) :
rule_( qi::lit( name ) >> rule[ ph::ref( rVar )=qi::_1 ] )
{}
};
template<typename T,typename ...Tail>
void mparse(const std::string& line,T& t,Tail& ...tail)
{
std::string::const_iterator f,l;
f=line.begin();
l=line.end();
// I would like to expand the rules here ...
//if(phrase_parse(f,l,expandBitwise(t,tail...),space ) && f==l)
if( phrase_parse(f, l, t.rule_, space ) && f==l )
std::cout<<"Parsed:"<<line<<std::endl;
else
std::cout<<"Syntax error:"<<line<<std::endl;
}
int main()
{
wrule_t rword=+~space;
std::string par1,par2,par3,par4;
TStruct r1( par1,"-a", rword );
TStruct r2( par2,"-b", rword );
TStruct r3( par3,"-c", rword );
TStruct r4( par4,"-d", rword );
mparse("abc 8.81" ,r1,r2,r3,r4);
mparse("-a atoken" ,r1,r2,r3,r4);
mparse("-b btoken" ,r1,r2,r3,r4);
mparse("-c ctoken" ,r1,r2,r3,r4);
mparse("-d dtoken" ,r1,r2,r3,r4);
return 0;
}
You accidentally returned the TStruct type from the
expandBitwise
helper. Fix it like so:If you want to expose attributes, the return type deduction rules become more involved. Basically, what you're doing is replicating the EDSL part of Spirit.
Let's swap stories...
Implementing the DSL mechanics for your option parser could be done more systematically by creating a new Proto Domain and actually creating the terminals. This would somehow appeal to me now.
Alternatively you could take this from another angle completely, using the Nabialek Trick. This happens to be an approach I played with just a few weeks ago, and I'll share with you the design I had come up with: https://gist.github.com/sehe/2a556a8231606406fe36#file-test-cpp
The important part is, where the grammar is "fixed":
The trick being in:
Where
shortNames
andlongNames
areqi::symbols
tables of parsers, built dynamically, based on a variadic list ofCliOptions
andCliFlags
(I pass them as a tuple, because I wanted to store the result inside theCliOption
struct as well).The
qi::lazy(_a)
invokes the parser that was stored in the symbol table.As a bonus, my CliOptions parser has a feature to generate "Usage" information as well. The builders for parse expressions as well as usage informations are extensible.
When invoked with some random arguments
-i 3 --completion -t --file=SOME.TXT -b huh?
, prints:As you can see, not all options have been implemented yet (notably,
--
to mark the end of the option list).Okay, so, I couldn't leave it alone :/
Turns out there was Undefined Behaviour involved, because of the way in which parser expressions were being passed to
expandBitwise
and being copied: Boost Proto expression templates weren't designed to be copied as they may contain references to temporaries, whose lifetime ends at the end of their containing full-expression.After a long (long) time of tweaking with
rule_.alias()
andboost::proto::deepcopy
I have reached the following solution (which, incidentally, doesn't need a helper function at all, anymore):The protection against UB is the
deepcopy_()
invocation, which is a trivial polymorphic callable adaptor forboost::proto::deepcopy
:With this code, lo and behold, the output becomes:
As a bonus, the code now allows you to use Spirit's builtin debug() capabilities (uncomment that line):
FULL CODE