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

后端 未结 1 1330
名媛妹妹
名媛妹妹 2020-12-11 11:35

I\'m trying to put together a simple command line parser with SPIRIT without semantic actions. I\'m using BOOST 1.52 but I would like to avoid using C++11 features. The gram

相关标签:
1条回答
  • 2020-12-11 12:07

    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 putting your variables one by one on the call to phrase_parse you have basically:

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

    Because of the way attribute propagation works in spirit this is what is happening:

    the whole tuple<size_t,bool,size_t> is assigned to your num1 (ignoring the bool and second size_t), after that spirit tries to assign the first string to your bool, resulting in the error you have.

    I believe the cleanest way to solve this is creating a custom struct to hold your result that 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 can't assign directly to rules declared with auto that have literals embedded in them (strings or numbers for example) without using boost::proto::deep_copy;

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

    There is a macro called BOOST_SPIRIT_AUTO that makes it easier to 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_);
    
    0 讨论(0)
提交回复
热议问题