3
votes

I'm quite new to Boost Spirit. Ideally I'd like to make sure two values in my grammar at the same using Phoenix. A dumbed down version of what I'm trying to get working would be a tuple where both ints are equal.

I'd like a string of "14,14,test" to parse but "14,12,test" to fail since 14 is not equal to 12. I'd like the code below to print:

Good: (14 14 test)
Fail

Currently both inputs will pass since I'm allowing and qi::int_ to parse for the second value without any checks.

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/spirit/include/qi_matches.hpp>

#include <string>
#include <vector>
#include <algorithm>

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;


int main(){


    std::vector<std::string> test_inputs = {"14,14,test","14,12,test"};

    std::for_each(test_inputs.begin(),test_inputs.end(),[](const std::string& input){
        boost::fusion::vector<int,int,std::string> result;
        int i(0);

        auto res = qi::parse(input.begin(),input.end(),
            //works but also parses "14,12,test"
            qi::int_[phx::ref(i) = qi::_1] >> qi::lit(",") >> qi::int_ >> qi::lit(",") >> +qi::char_,
            //Fails to compile
            //qi::int_[phx::ref(i) = qi::_1] >> qi::lit(",") >> qi::int_(phx::val(i)) >> qi::lit(",") >> +qi::char_,
            //qi::int_[phx::ref(i) = qi::_1] >> qi::lit(",") >> qi::int_(phx::ref(i)) >> qi::lit(",") >> +qi::char_,
            result);
        if(res){
            std::cout << "Good: " << result << std::endl;
        }
        else{
            std::cout << "Fail" << std::endl;
        }
    });         
}

I can use phoenix::ref to capture first value but I can't figure out to test my second qi::int_ parser for the value of of i. I've tried using phoenix::val and phoenix::ref with no luck compiling. Ideally I'd like to capture the value in the second int and thought the int_ parser would take a lazy literal from Phoenix.

Thanks for any and all help on this.

1
The alternative that uses phx::ref twice that you have marked as not compiling works if you define BOOST_SPIRIT_USE_PHOENIX_V3 when using g++ 4.8.1 and boost 1.54. I would recommend using that define always, it solves lots of problems.llonesmiz
I meant defining before the inclusion of headers. You can see it here. In any case I think there should be alternative solutions, so hopefully you'll get an informative answer.llonesmiz
The define gets the code to compile for me in both gcc 4.7 and Clang. Thanks!Joel

1 Answers

5
votes

The simplest fix is to supply the value i picked up from the first qi::int_ to the second one, i.e.:

qi::int_[phx::ref(i) = qi::_1] >> qi::lit(",") >> qi::int_(phx::ref(i)) >> qi::lit(",") >> +qi::char_,

qi::_int with a parameter expects the supplied value.

Another alternative to consider is the use of a rule-local variable. You could do this:

typedef boost::fusion::vector<int,int,std::string> result_t;
result_t result;
qi::rule<std::string::const_iterator, result_t(),
     boost::spirit::qi::locals<int> > r;
r %= qi::int_[qi::_a = qi::_1] >> qi::lit(",") >> qi::int_(qi::_a) >> qi::lit(",") >> +qi::char_;

Then use "r" in place of your rule. Although I suppose if you just intend to run the rule directly using qi::parse this doesn't make sense.