Search code examples
c++c++14boost-spiritboost-spirit-x3

parse kleene operator to a set of alternatives, adaptor? with spirit x3


I've learned a lot in the last couple of weeks about this stuff, but not enough. The code below compiles and runs but the code in TEST_ADAPT is incomplete, I'm not sure how to make the connection.

The object is to parse into a plane jane container that has no variant dependency. I have figured out I can get a tuple of references to my storage which spirit likes well enough. (see TEST_REF). The kleene operator is looking for a single sequential container but on a set of alternatives, that doesn't look to be possible. So I guess I need to hand it something that is a proxy for that container but has gear work to locate in a tuple the destination references.

I think it will be a great exercise for me to write this ContainerAdaptor even if it is the wrong way to approach this. So I'm wondering if I'm in right field or on the right track.

The best I know I can complete is to use the TEST_VECT method and make a pass over the vector to copy the data into my ALL container. But that's just not right.

Update: I have made Target::All fusion adapted and made ContainerAdaptor partially functional. Enough so that the kleene operator accepts it. I should be able to connect to the Target::All object, maybe...

#include <iostream>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>

//parse kleene operator to a set of alternatives, adaptor? with spirit x3
#define TEST_VECT
#define TEST_REF
#define TEST_ADAPT
// l.......................................................................
namespace Target {
    struct Int
    {
        int int_val;
    };
    using IntVect = std::vector<Int>;

    struct Word
    {
        std::string word_val;
    };
    using WordVect = std::vector<Word>;

    struct All
    {
        IntVect int_vect;
        WordVect word_vect;
    };
}

BOOST_FUSION_ADAPT_STRUCT(Target::Int, int_val)
BOOST_FUSION_ADAPT_STRUCT(Target::Word, word_val)
BOOST_FUSION_ADAPT_STRUCT(Target::All, int_vect, word_vect)

std::ostream& operator << (std::ostream& o, const Target::Int& in) { o << in.int_val; return o; }
std::ostream& operator << (std::ostream& o, const Target::Word& in) { o << in.word_val; return o; }
std::ostream& operator << (std::ostream& o, const Target::IntVect& in) { for( auto& i : in ) o << i << " "; return o; }
std::ostream& operator << (std::ostream& o, const Target::WordVect& in) { for (auto& i : in) o << i << " "; return o; }

#define DEF_RULE( RuleName, Attr ) static auto const RuleName = rule<struct Attr##_def, Attr>( #RuleName )
namespace Target {

    using namespace boost::spirit::x3;

    auto const bare_word = lexeme[+char_("a-z")];

    DEF_RULE(int_rule, Int) = int_;
    DEF_RULE(word_rule, Word) = bare_word;

    auto const int_vect_rule= "int" >> *int_rule;
    auto const word_vect_rule= "word" >> *(word_rule - "int");

    //another test
    DEF_RULE(f_int_vect_rule, IntVect) = int_vect_rule;
    DEF_RULE(f_word_vect_rule, IntVect) = word_vect_rule;

}//namespace Target

namespace Target {
    struct Printer {

        Printer(std::ostream& out) : out(out) {};
        using result_type = void;

        void operator()(const IntVect& expression) {
            out << "IntVect: ";
            for (auto& t : expression)
                out << t << " ";
            out << std::endl;
        }
        void operator()(const WordVect& expression) {
            out << "Word: ";
            for (auto& t : expression)
                out << t << " ";
            out << std::endl;
        }
    private:
        std::ostream& out;
    };
}//namespace Target

template<class Arg>
class ContainerAdaptor
{
public:
    ContainerAdaptor(Arg& arg) :arg(arg) { }
    typedef boost::spirit::x3::variant<Target::IntVect,Target::WordVect> value_type;
    typedef size_t size_type;
    struct Vis : public boost::static_visitor<>
    {
        void operator()(const Target::IntVect & i) const
        {
            std::cout << i << std::endl;
        }
        void operator()(const Target::WordVect & i) const
        {
            std::cout << i << std::endl;
        }

    };
    void insert(value_type* e, const value_type& v) {
        std::cout << "haha! ";
        boost::apply_visitor(Vis(), v);
    }
    value_type* end() { return nullptr; }
    value_type* begin() { return nullptr; }
    size_t size;

private:
    Arg & arg;
};

int main()
{
    using namespace Target;
    std::string thestr("word test more int 1 2 3 4 word this and that int 5 4 int 99 22");
    std::string::iterator end = thestr.end();

#if defined(TEST_ADAPT)
    {
        std::cout << "\nTEST_ADAPT\n";
        std::string::iterator begin = thestr.begin();
        All all;
        auto fwd = std::forward_as_tuple(all.word_vect, all.int_vect);

        ContainerAdaptor<All>attr( all );
        phrase_parse(begin, end, *( int_vect_rule | word_vect_rule), space, attr);
        Printer printer(std::cout);
    }
#endif
#if defined(TEST_VECT)
    {
        std::cout << "TEST_VECT\n";
        std::string::iterator begin = thestr.begin();
        using Vars = variant<Target::IntVect, Target::WordVect>;
        std::vector< Vars > a_vect;

        bool r = phrase_parse(begin, end, *( int_vect_rule | word_vect_rule), space, a_vect);

        Printer printer(std::cout);
        for (auto& i : a_vect)
            i.apply_visitor(printer);
    }
#endif
#if defined(TEST_REF)
    {
        std::cout << "\nTEST_REF\n";
        std::string::iterator begin = thestr.begin();
        All all;
        auto fwd = std::forward_as_tuple(all.word_vect,all.int_vect);
        phrase_parse(begin, end, word_vect_rule >> int_vect_rule, space, fwd);
        Printer printer(std::cout);
        std::_For_each_tuple_element(fwd, printer);
    }
#endif
    return 0;
}

Solution

  • The ContainerAdaptor Hack

    Sufficiently simplified, it works:

    Live On Coliru

    #include <iostream>
    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/home/x3.hpp>
    #include <boost/spirit/home/x3/support/ast/variant.hpp>
    
    namespace Target {
        struct Int { int int_val; };
        struct Word { std::string word_val; };
    
        using IntVect = std::vector<Int>;
        using WordVect = std::vector<Word>;
    
        struct All {
            IntVect int_vect;
            WordVect word_vect;
        };
    }
    
    BOOST_FUSION_ADAPT_STRUCT(Target::Int, int_val)
    BOOST_FUSION_ADAPT_STRUCT(Target::Word, word_val)
    
    std::ostream& operator << (std::ostream& o, const Target::Int& in) { o << in.int_val; return o; }
    std::ostream& operator << (std::ostream& o, const Target::Word& in) { o << in.word_val; return o; }
    std::ostream& operator << (std::ostream& o, const Target::IntVect& in) { for( auto& i : in ) o << i << " "; return o; }
    std::ostream& operator << (std::ostream& o, const Target::WordVect& in) { for (auto& i : in) o << i << " "; return o; }
    
    namespace Target {
        using namespace boost::spirit::x3;
    
        static auto const int_rule  = rule<struct Int_def, Int>("int_rule")    = int_;
        static auto const word_rule = rule<struct Word_def, Word>("word_rule") = lexeme[+char_("a-z")];
    
        static auto const int_vect_rule  = "int" >> *int_rule;
        static auto const word_vect_rule = "word" >> *(word_rule - "int");
    }
    
    template<class Arg> struct ContainerAdaptor
    {
        typedef boost::spirit::x3::variant<Target::IntVect,Target::WordVect> value_type;
    
        void insert(value_type* /*e*/, const value_type& v) {
            std::cout << "haha! ";
            struct Vis {
                //using result_type = void;
    
                void operator()(const Target::IntVect & i) const { std::cout << i << std::endl; }
                void operator()(const Target::WordVect & i) const { std::cout << i << std::endl; }
            };
            boost::apply_visitor(Vis(), v);
        }
    
        value_type* end()   { return nullptr; }
        value_type* begin() { return nullptr; }
    
        Arg & arg;
    };
    
    int main() {
        using namespace Target;
        std::string const thestr("word test more int 1 2 3 4 word this and that int 5 4 int 99 22");
    
        All all;
        ContainerAdaptor<All> attr { all };
    
        if (phrase_parse(begin(thestr), end(thestr), *( int_vect_rule | word_vect_rule), space, attr)) {
            std::cout << "Parsed: \n";
            std::cout << all.int_vect << "\n";
            std::cout << all.word_vect << "\n";
        }
    }
    

    Prints:

    haha! test more 
    haha! 1 2 3 4 
    haha! this and that 
    haha! 5 4 
    haha! 99 22 
    Parsed: 
    

    Why Not Semantic Actions?

    Live On Coliru

    #include <iostream>
    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/home/x3.hpp>
    
    namespace Target {
        struct Int { int int_val; };
        struct Word { std::string word_val; };
    
        using IntVect = std::vector<Int>;
        using WordVect = std::vector<Word>;
    
        struct All {
            IntVect int_vect;
            WordVect word_vect;
        };
    }
    
    BOOST_FUSION_ADAPT_STRUCT(Target::Int, int_val)
    BOOST_FUSION_ADAPT_STRUCT(Target::Word, word_val)
    
    std::ostream& operator << (std::ostream& o, const Target::Int& in) { o << in.int_val; return o; }
    std::ostream& operator << (std::ostream& o, const Target::Word& in) { o << in.word_val; return o; }
    std::ostream& operator << (std::ostream& o, const Target::IntVect& in) { for( auto& i : in ) o << i << " "; return o; }
    std::ostream& operator << (std::ostream& o, const Target::WordVect& in) { for (auto& i : in) o << i << " "; return o; }
    
    namespace x3 = boost::spirit::x3;
    namespace Target {
        using namespace x3;
    
        static auto const int_rule  = rule<struct Int_def, Int>("int_rule")    = int_;
        static auto const word_rule = rule<struct Word_def, Word>("word_rule") = lexeme[+char_("a-z")];
    
        static auto const int_vect_rule  = "int" >> *int_rule;
        static auto const word_vect_rule = "word" >> *(word_rule - "int");
    }
    
    int main() {
        std::string const thestr("word test more int 1 2 3 4 word this and that int 5 4 int 99 22");
    
        Target::All all;
    
        struct {
            Target::All& _r;
            void operator()(Target::IntVect const&v) const { _r.int_vect.insert(_r.int_vect.end(), v.begin(), v.end()); }
            void operator()(Target::WordVect const&v) const { _r.word_vect.insert(_r.word_vect.end(), v.begin(), v.end()); }
        } push_back { all };
    
        auto unary = [](auto f) { return [f](auto& ctx) { return f(x3::_attr(ctx)); }; };
        auto action = unary(push_back);
    
        if (phrase_parse(begin(thestr), end(thestr), *(Target::int_vect_rule[action] | Target::word_vect_rule[action]), x3::space)) {
            std::cout << "Parsed: \n";
            std::cout << all.int_vect << "\n";
            std::cout << all.word_vect << "\n";
        }
    }
    

    Prints

    Parsed: 
    1 2 3 4 5 4 99 22 
    test more this and that 
    

    Using Traits

    Re-introducing the variant "value_type":

    Live On Coliru

    #include <iostream>
    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/home/x3.hpp>
    
    namespace Target {
        struct Int { int int_val; };
        struct Word { std::string word_val; };
    
        using IntVect = std::vector<Int>;
        using WordVect = std::vector<Word>;
    
        struct All {
            IntVect int_vect;
            WordVect word_vect;
        };
    }
    
    BOOST_FUSION_ADAPT_STRUCT(Target::Int, int_val)
    BOOST_FUSION_ADAPT_STRUCT(Target::Word, word_val)
    
    std::ostream& operator << (std::ostream& o, const Target::Int& in) { o << in.int_val; return o; }
    std::ostream& operator << (std::ostream& o, const Target::Word& in) { o << in.word_val; return o; }
    std::ostream& operator << (std::ostream& o, const Target::IntVect& in) { for( auto& i : in ) o << i << " "; return o; }
    std::ostream& operator << (std::ostream& o, const Target::WordVect& in) { for (auto& i : in) o << i << " "; return o; }
    
    namespace boost { namespace spirit { namespace x3 { namespace traits {
    
        template<>
        struct container_value<Target::All> {
            using type = boost::variant<Target::IntVect, Target::WordVect>;
        };
    
        template<>
        struct push_back_container<Target::All> {
            template <typename V>
                static bool call(Target::All& c, V&& v) {
                    struct {
                        Target::All& _r;
                        void operator()(Target::IntVect const&v) const { _r.int_vect.insert(_r.int_vect.end(), v.begin(), v.end()); }
                        void operator()(Target::WordVect const&v) const { _r.word_vect.insert(_r.word_vect.end(), v.begin(), v.end()); }
                    } vis {c};
                    boost::apply_visitor(vis, v);
                    return true;
                }
        };
    
    } } } }
    
    namespace x3 = boost::spirit::x3;
    namespace Target {
        using namespace x3;
    
        static auto const int_rule  = rule<struct Int_def, Int>("int_rule")    = int_;
        static auto const word_rule = rule<struct Word_def, Word>("word_rule") = lexeme[+char_("a-z")];
    
        static auto const int_vect_rule  = "int" >> *int_rule;
        static auto const word_vect_rule = "word" >> *(word_rule - "int");
    }
    
    int main() {
        std::string const thestr("word test more int 1 2 3 4 word this and that int 5 4 int 99 22");
    
        Target::All all;
    
        if (phrase_parse(begin(thestr), end(thestr), *(Target::int_vect_rule | Target::word_vect_rule), x3::space, all)) {
            std::cout << "Parsed: \n";
            std::cout << all.int_vect << "\n";
            std::cout << all.word_vect << "\n";
        }
    }
    

    Prints

    Parsed: 
    1 2 3 4 5 4 99 22 
    test more this and that