5

My goal is to "dynamically" build expression composed of simple permutated expressions but I cannot get it fully functional. Namely these two sequences show that it does not work as expected:

-b btoken -c ctoken -d dtoken -a atoken
-c ctoken -d dtoken -a atoken -b btoken

The ultimate goal is to be able dynamically build expressions that can parse different types as well: int,float,double... Your advice is appreciated :-)

#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;
namespace ph = boost::phoenix;
namespace ascii = boost::spirit::ascii;

typedef qi::rule<std::string::const_iterator,ascii::space_type> mrule_t;
typedef qi::rule<std::string::const_iterator,std::string() >    wrule_t;

struct TStruct
{
    mrule_t     rule_;
    template<typename T,typename R>
    TStruct( T& rVar,const std::string&name, const R& rule ) :
        rule_( qi::lit(name) >> rule[ ph::ref(rVar) = qi::_1 ] )
    {
        rule_.name(name);
    }
};

bool mparse(const std::string& line,std::vector< TStruct >& args )
{
    mrule_t parser = qi::eps(false);
    for( const auto &argsx : args )
        parser = argsx.rule_.copy() ^ parser.copy();
    // BOOST_SPIRIT_DEBUG_NODES( (parser) );
    auto f = begin(line), l=end(line);
    return  qi::phrase_parse( f, l, parser, ascii::space ) && f==l;
}

int main()
{
    wrule_t rword=+~ascii::space;

    std::string par1,par2,par3,par4;

    std::vector< TStruct > args{
        { par1, "-a", rword },
        { par2, "-b", rword },
        { par3, "-c", rword },
        { par4, "-d", rword }
    };

    std::vector< std::string > inputs{
        "-a atoken -b btoken -c ctoken -d dtoken",
        "-b btoken -c ctoken -d dtoken -a atoken",
        "-b btoken -c ctoken -d dtoken",
        "-b btoken -c ctoken",
        "-c ctoken -d dtoken -a atoken -b btoken",
        "-d dtoken -a atoken -b btoken -c ctoken",
        "-a atoken",
        "-b btoken",
        "-c ctoken",
        "-d dtoken"
    };

    for ( const auto& input : inputs )
    {
        std::cout << "processing input:" << input << std::endl;
        par1=par2=par3=par4="";
        if( mparse( input,args ) )
        {
            std::cout << "par1:" << par1 << std::endl;
            std::cout << "par2:" << par2 << std::endl;
            std::cout << "par3:" << par3 << std::endl;
            std::cout << "par4:" << par4 << std::endl;
        }
        std::cout << std::endl;
    }

    return 0;
}
G. Civardi
  • 667
  • 5
  • 12
  • As sehe recommended [here](http://stackoverflow.com/questions/16177184/generating-spirit-parser-expressions-from-a-variadic-list-of-alternative-parser), I think the best approach to this situation is using the nabialek trick. You can see an example [here](http://coliru.stacked-crooked.com/view?id=2c2cf4c799f1c5fcbb97988398066c89-b74ad864f94230bc315caac175e13350). My experiences with the permutation parser have not been very good, I usually tend to avoid it. I hope you get a good answer. – llonesmiz Aug 18 '13 at 22:11
  • @cv_and_he - thank you for your comment. I have fairly simple rules to bind and I wonder what `parser=argsx.rule_.copy()^parser.copy()` does. And if anybody can come up with a "new" trick :-) – G. Civardi Aug 19 '13 at 05:42
  • @cv_and_he - Hi! thanks for looking into my "academic" issue and don't worry about silly suggestions - even silly suggestions help to find the solution :-) – G. Civardi Aug 19 '13 at 17:20
  • @cv_and_he I know that there are various "workarounds" but I would like to proceed into a "new" territory to better understand how the whole spirit parsing concept works and what are for instance the limits... – G. Civardi Aug 19 '13 at 17:28
  • @cv_and_he this really deserves to be an answer – sehe Aug 21 '13 at 20:06
  • @cv_and_he @sehe - cv_and_he's proposal(s) should not end up hidden in comments so that the effort can be rewarded. I would like to see "dynamic" - building the expression at runtime - as I won't have the data to fill `TStruct` at compile time but at runtime. – G. Civardi Aug 22 '13 at 06:34
  • @sehe I updated the question title to remove any ambiguity. – G. Civardi Aug 22 '13 at 06:49

1 Answers1

2

Protecting the answer-worthy comment by @cv_and_he from future over-zealous mods (as a community-wiki, because this is beyond my understanding):

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <iostream>
#include <iomanip>
#include <memory>

#include <boost/spirit/include/qi_core.hpp>
#include <boost/spirit/include/qi_nonterminal.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

namespace qi = boost::spirit::qi;
namespace ph = boost::phoenix;
namespace ascii = boost::spirit::ascii;

typedef qi::rule<std::string::const_iterator,ascii::space_type> mrule_t;
typedef qi::rule<std::string::const_iterator,std::string() >    wrule_t;

struct TStruct
{
    mrule_t     rule_;
    template<typename T, typename R>
    TStruct( T& rVar,const std::string&name, const R& rule ) :
        rule_( rule[ ph::ref(rVar) = qi::_1 ] )
    {
        rule_.name(name);
        qi::debug(rule_);
    }
};

bool mparse(const std::string& line, const std::vector<TStruct>& args )
{
   qi::symbols<char,const mrule_t*> options;
    for( const auto &argsx : args )
        options.add(argsx.rule_.name(),&(argsx.rule_));
    auto f = begin(line), l=end(line);
    qi::rule<std::string::const_iterator,qi::locals<const mrule_t*>,ascii::space_type> parser = options[qi::_a=qi::_1] >> qi::lazy(*qi::_a);

    return  qi::phrase_parse( f, l, +parser, ascii::space ) && f==l;
}

int main()
{
    wrule_t rword=+~ascii::space;

    std::string par1,par2,par3,par4;

    std::vector< TStruct > args{
        { par1, "-a", rword },
        { par2, "-b", rword },
        { par3, "-c", rword },
        { par4, "-d", rword }
    };

    std::vector< std::string > inputs{
        "-a atoken -b btoken -c ctoken -d dtoken",
        "-b btoken -c ctoken -d dtoken -a atoken",
        "-b btoken -c ctoken -d dtoken",
        "-b btoken -c ctoken",
        "-c ctoken -d dtoken -a atoken -b btoken",
        "-d dtoken -a atoken -b btoken -c ctoken",
        "-a atoken",
        "-b btoken",
        "-c ctoken",
        "-d dtoken"
    };

    for ( const auto& input : inputs )
    {
        std::cout << "processing input:" << input << std::endl;
        par1=par2=par3=par4="";
        if( mparse( input,args ) )
        {
            std::cout << "par1:" << par1 << std::endl;
            std::cout << "par2:" << par2 << std::endl;
            std::cout << "par3:" << par3 << std::endl;
            std::cout << "par4:" << par4 << std::endl;
            std::cout << std::endl << std::endl;
        }
        std::cout << std::endl;
    }

    return 0;
}

Live Example.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304