Question

I have the following rule, inside my boost::spirit grammar;

rule = (qi::token(ABSTRACT) ^ qi::token(STATIC) ^ qi::token(FINAL))

This construct has the type of;

boost::fusion::vector3<boost::optional<boost::iterator_range<std::string::iterator>>, boost::optional<boost::iterator_range<std::string::iterator>>, boost::optional<boost::iterator_range<std::string::iterator>>>

However, what I would rather have is;

boost::fusion::vector3<bool, bool, bool>

Where each bool have the value of the corresponding boost::optional<...> operator bool() const, with a single case, I'd use qi::matches, as suggested here. However applying this approach yields;

(qi::matches[qi::raw_token(ABSTRACT)] ^ qi::matches[qi::raw_token(STATIC)] ^ qi::matches[qi::raw_token(FINAL)])

Which has the type;

boost::fusion::vector3<boost::optional<bool>, boost::optional<bool>, boost::optional<bool>>

Which is indeed not what I wanted, so how would I go about, matching these three tokens in random order, and getting whether they were matched as booleans?

Was it helpful?

Solution

I'm assuming that you don't really need a fusion::vector<bool,bool,bool> and you just want it to work. If this assumption is wrong simply say something in the comments.

As you can see in the documentation the attribute of the permutation parser is tuple<optional<A>,optional<B>>. In your case this translates to fusion::vector<optional<bool>,optional<bool>,optional<bool>>. Thanks to the way Spirit works with attributes this vector is compatible with a simple fusion::vector<bool,bool,bool>. In this case this means that every non-empty component of the optional vector (the ones that are actually matched by the permutation parser) is assigned to its corresponding one of the plain bool vector, and the rest of the components in this latter vector remain unchanged.

The problem I see in you example is in the way the qi::matches directive(it always succeeds) and the permutation parser(each element in the permutation set may occur at most once) work. You can see this problem in the output of the program below:

USING MATCHES: final abstract static

<abstract1>

  <try>final abstract stati</try>

  <success>final abstract stati</success>

  <attributes>[0]</attributes>

</abstract1>

<static1>

  <try>final abstract stati</try>

  <success>final abstract stati</success>

  <attributes>[0]</attributes>

</static1>

<final1>

  <try>final abstract stati</try>

  <success> abstract static</success>

  <attributes>[1]</attributes>

</final1>

The first rule it tries is abstract1, since the input at that point is "final", the argument of qi::matches fails and thus qi::matches succeeds returning false. Since abstract1 has already succeeded, the permutation parser will never try it again. Next static1 is tried, with the exact same result. After that the input is still "final" and the rule tried is final1, it succeeds returning true. All three arguments have succeeded, and so the permutation parser ends, leaving "abstract static" unparsed.

One way to do what you want is using something similar to the second option sehe recommends in his answer. You'll need to use (qi::raw_token(ABSTRACT) >> qi::attr(true)) ^ (qi::raw_token(STATIC) >> qi::attr(true)) ^ (qi::raw_token(FINAL) >> qi::attr(true)). KEYWORD >> qi::attr(true) will succeed returning true when KEYWORD matches, and fail otherwise. As mentioned earlier the components of the final vector that correspond with empty optionals remain unchanged so you need to make sure that they are initialized to false (using a default constructor for example).

Running on Coliru:

#include <iostream>
#include <string>
#include <vector>

#define BOOST_SPIRIT_DEBUG

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

struct class_info
{
    class_info():abstract_(),static_(),final_(){}
    bool abstract_;
    bool static_;
    bool final_;
};

BOOST_FUSION_ADAPT_STRUCT
(
    class_info,
    (bool, abstract_)
    (bool, static_)
    (bool, final_)
)

namespace qi=boost::spirit::qi;

template <typename Parser>
void parse_triplet(const std::string& test, const Parser& parser)
{
    std::string::const_iterator iter=test.begin(), end=test.end();
    class_info parsed_class;

    bool result=qi::phrase_parse(iter,end,parser,qi::space,parsed_class);

    if(!result)
    {
        std::cout << "The triplet could not be parsed. Unparsed: " << std::string(iter,end) << std::endl;
    }
    else
    {
        if(iter==end)
        {
            std::cout << "A triplet was parsed." << std::endl;
            std::cout << parsed_class.abstract_ << parsed_class.static_ << parsed_class.final_ << std::endl;
        }
        else
        {
            std::cout << "A triplet was parsed, but part of the input remained unparsed." << std::endl;
            std::cout << parsed_class.abstract_ << parsed_class.static_ << parsed_class.final_ << std::endl;
            std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
        }
    }

}


int main()
{
    std::vector<std::string> tests;
    tests.push_back("final abstract static");
    tests.push_back("final abstract");
    tests.push_back("static");
    tests.push_back("static abstract");
    qi::rule<std::string::const_iterator,bool(),qi::space_type> abstract1, abstract2, static1, static2, final1, final2;
    abstract1 = qi::matches[qi::lit("abstract")];
    abstract2 = qi::lit("abstract") >> qi::attr(true);
    static1 = qi::matches[qi::lit("static")];
    static2 = qi::lit("static") >> qi::attr(true);
    final1 = qi::matches[qi::lit("final")];
    final2 = qi::lit("final") >> qi::attr(true);

    BOOST_SPIRIT_DEBUG_NODES( (abstract1)(abstract2)(static1)(static2)(final1)(final2) );

    for(std::size_t cont=0; cont < tests.size(); ++cont)
    {
            //THIS IS WRONG
        std::cout << "USING MATCHES: " << tests[cont] << std::endl;
        parse_triplet(tests[cont],abstract1 ^ static1 ^ final1);

            //THIS WORKS
        std::cout << "USING EXPLICIT ATTRIBUTE: " << tests[cont] << std::endl;
        parse_triplet(tests[cont],abstract2 ^ static2 ^ final2);


    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top