Boost :: spirit :: qi permutation score and synthesized attributes

I am trying to compile a simple command line parser with SPIRIT without semantic actions. I am using BOOST 1.52, but I would like to avoid using the features of C ++ 11. The grammar has the following syntax:

[-p num1] [-j] [--jobs num2] str1 str2 

Additional options can be in any order. I have successfully analyzed only optional parameters. As soon as I add the additional required two parsers, it breaks. It breaks even when I try to write the "rstart" attributes explicitly and avoid type inference with "auto". Any help or suggestion is greatly appreciated!

 #include <iostream> #include <string> #include <vector> #include <iterator> #include <boost/spirit/include/qi.hpp> #include <boost/optional.hpp> #include <boost/fusion/include/boost_tuple.hpp> bool parse_line( const std::string&str,bool useStart1 ) { bool rc=false; namespace qi = boost::spirit::qi; using boost::spirit::ascii::space_type; using boost::spirit::ascii::space; using boost::spirit::ascii::char_; std::string::const_iterator iter( str.begin() ); std::size_t num1 =88; bool bool1 =false; std::size_t num2 =88; std::string str1,str2; qi::rule< std::string::const_iterator,std::string() > rstring=+~space; qi::rule< std::string::const_iterator,std::size_t() ,space_type > rOption1=qi::lit( "-p" ) >> qi::int_; qi::rule< std::string::const_iterator,bool() ,space_type > rOption2=qi::lit( "-j" ) >> qi::attr(true); qi::rule< std::string::const_iterator,std::size_t() ,space_type > rOption3=qi::lit( "--jobs" ) >> qi::int_; #if defined(AAA) qi::rule< std::string::const_iterator, boost::spirit::ascii::space_type, boost::tuple< boost::optional<std::size_t>, boost::optional<bool>, boost::optional<std::size_t > > > #endif auto rstart1 = ( rOption1 ^ rOption2 ^ rOption3 ) ; auto rstart2 = ( rOption1 ^ rOption2 ^ rOption3 ) >> rstring >> rstring; if( useStart1 ) qi::phrase_parse( iter,str.end(), ( qi::lit( "-p" ) >> qi::int_ ) ^ ( qi::lit( "-j" ) >> qi::attr(true) ) ^ ( qi::lit( "--jobs" ) >> qi::int_ ),space,num1,bool1,num2); else { // qi::phrase_parse( // iter,str.end(),rstart2,space,num1,bool1,num2,str1,str2); } if(iter==str.begin()) iter=str.begin(); //NOP else if(iter!=str.end()) std::cerr<<"syntax error: "<<std::string(iter,str.end())<<"!\n\n"; else rc=true; std::cout << "num1:" << num1 << std::endl; std::cout << "bool1:"<< bool1 << std::endl; std::cout << "num2:" << num2 << std::endl; std::cout << "str1:" << str1 << std::endl; std::cout << "str2:" << str2 << std::endl; return rc; } int main( int argc,char**argv ) { std::vector< std::string > testData1; testData1.push_back( "-p 100 -j" ); testData1.push_back( "-j -p 100 --jobs 16" ); testData1.push_back( "--jobs 16 -j -p 100" ); for( std::vector< std::string >::const_iterator it=testData1.begin(); it!=testData1.end(); ++it ) { std::cout << "\nparsing string:" << *it << std::endl; parse_line( *it,true ); } std::vector< std::string > testData2; testData2.push_back( "-p 100 -j ifile ofile" ); testData2.push_back( "-j -p 100 --jobs 16 ifile ofile" ); testData2.push_back( "--jobs 16 -j -p 100 ifile ofile" ); for( std::vector< std::string >::const_iterator it=testData2.begin(); it!=testData2.end(); ++it ) { std::cout << "\nparsing string:" << *it << std::endl; parse_line( *it,false ); } return 0; } 
+2
source share
1 answer

The problem you are facing is that the attribute of your combined rule is basically:

 tuple< tuple<size_t,bool,size_t>, std::string, std::string > 

and by placing the variables one at a time in the parse_phrase call, you basically:

 tuple< size_t, bool, size_t, std::string, std::string > 

Due to how attribute propagation works in spirit, this is what happens:

the integer tuple<size_t,bool,size_t> assigned to your num1 (ignoring bool and second size_t), after which the spirit tries to assign the first row to your bool, as a result you get.

I believe that the cleanest way to solve this problem is to create a custom structure to store your result, which reflects the structure of your rules.

 #include <iostream> #include <string> #include <vector> #include <iterator> #include <boost/spirit/include/qi.hpp> #include <boost/fusion/include/adapt_struct.hpp> struct optional_command_line_options { int num1; bool bool1; int num2; }; struct command_line_options { optional_command_line_options opt; std::string str1; std::string str2; }; BOOST_FUSION_ADAPT_STRUCT( optional_command_line_options, (int, num1) (bool, bool1) (int, num2) ) BOOST_FUSION_ADAPT_STRUCT( command_line_options, (optional_command_line_options, opt) (std::string, str1) (std::string, str2) ) bool parse_line( const std::string&str ) { bool rc=false; namespace qi = boost::spirit::qi; using boost::spirit::ascii::space; using boost::spirit::ascii::char_; std::string::const_iterator iter( str.begin() ); command_line_options options; options.opt.num1=88; options.opt.bool1=false; options.opt.num2=88; qi::rule< std::string::const_iterator, std::string() > rstring=+~space; qi::rule<std::string::const_iterator, boost::spirit::ascii::space_type,optional_command_line_options() > trule; trule= ( qi::lit( "-p" ) >> qi::int_ ) ^ ( qi::lit( "-j" ) >> qi::attr(true) ) ^ ( qi::lit( "--jobs" ) >> qi::int_ ) ; qi::rule< std::string::const_iterator, boost::spirit::ascii::space_type, command_line_options() >arule; arule = -trule >> rstring >> rstring; bool result=qi::phrase_parse( iter,str.end(), arule, space, options ); if(result && iter==str.end()) { std::cout << "Parse successful." << std::endl; rc=true; } else { std::cerr<<"syntax error: "<<std::string(iter,str.end())<<"!\n\n"; } std::cout << std::boolalpha; std::cout << "num1:" << options.opt.num1 << std::endl; std::cout << "bool1:"<< options.opt.bool1 << std::endl; std::cout << "num2:" << options.opt.num2 << std::endl; std::cout << "str1:" << options.str1 << std::endl; std::cout << "str2:" << options.str2 << std::endl; return rc; } int main( int /*argc*/,char**/*argv*/ ) { std::vector< std::string > testData; testData.push_back( "-p 100 -j ifile ofile" ); testData.push_back( "-j -p 100 --jobs 16 ifile ofile" ); testData.push_back( "--jobs 16 -j -p 100 ifile ofile" ); testData.push_back( "--jobs 16 -p 100 ifile ofile" ); testData.push_back( "ifile ofile" ); for( std::vector< std::string >::const_iterator it=testData.begin(); it!=testData.end(); ++it ) { std::cout << "\nparsing string:" << *it << std::endl; parse_line( *it ); } return 0; } 

Running on LWS .

PS: you cannot directly assign rules declared using auto that have literals embedded in them (for example, strings or numbers) without using boost :: proto :: deep_copy;

 auto trule = boost::proto::deep_copy(qi::lit( "-p" ) >> qi::int_); 

There is a macro BOOST_SPIRIT_AUTO that simplifies its use:

 #define BOOST_SPIRIT_AUTO(domain_, name, expr) \ typedef boost::proto::result_of:: \ deep_copy<BOOST_TYPEOF(expr)>::type name##_expr_type; \ BOOST_SPIRIT_ASSERT_MATCH( \ boost::spirit::domain_::domain, name##_expr_type); \ BOOST_AUTO(name, boost::proto::deep_copy(expr)); BOOST_SPIRIT_AUTO(qi,trule,qi::lit( "-p" ) >> qi::int_); 
+3
source

All Articles