Boost::spirit::qi排列解析器和合成属性

boost::spirit::qi permutation parser and synthesized attributes

本文关键字:属性 spirit qi 排列 Boost      更新时间:2023-10-16

我正试图将一个简单的命令行解析器与SPIRIT放在一起,而不需要语义操作。我正在使用BOOST 1.52,但我想避免使用c++ 11的功能。该语法具有以下语法:

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

可选参数可以是任意顺序。我只成功解析了可选参数。一旦我添加了额外的强制两个字符串解析器,它就中断了。当我试图显式地写下"rstart"属性并避免使用"auto"进行类型演绎时,它也会中断。任何帮助或建议是非常感谢!

#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())<<"!nn";
    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;
}

您面临的问题是,您的组合规则的属性基本上是:

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

并且通过将变量一个接一个地放在对phrase_parse的调用中,基本上可以得到:

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

由于属性传播在精神上的工作方式,这就是正在发生的事情:

将整个tuple<size_t,bool,size_t>分配给您的num1(忽略bool和第二个size_t),之后spirit试图将第一个字符串分配给bool,导致您遇到的错误。

我认为解决这个问题最干净的方法是创建一个自定义结构来保存反映规则结构的结果。

#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())<<"!nn";
    }
     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;
}

运行在LWS上。

PS:不使用boost::proto::deep_copy;

你不能直接赋值给用auto声明的有文字嵌入的规则(例如字符串或数字)
auto trule = boost::proto::deep_copy(qi::lit( "-p" ) >> qi::int_);

有一个名为BOOstrongPIRIT_AUTO的宏使它更容易使用:

#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_);