boost::spirit::qi strange attribute print out

77 views Asked by At
#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;

namespace test {
    template< typename Rng,typename Expr >
    bool parse(Rng const& rng,Expr const& expr) noexcept {
        auto itBegin = boost::begin(rng);
        auto itEnd = boost::end(rng);
        try {
            return qi::parse(itBegin,itEnd,expr);
        } catch(qi::expectation_failure<decltype(itBegin)> const& exfail) {
            exfail;
            return false;
        }
    }
    template< typename Rng,typename Expr,typename Attr >
    bool parse(Rng const& rng,Expr const& expr,Attr& attr) noexcept {
        auto itBegin = boost::begin(rng);
        auto itEnd = boost::end(rng);
        try {
            return qi::parse(itBegin,itEnd,expr,attr);
        } catch(qi::expectation_failure<decltype(itBegin)> const&) {
            return false;
        }
    }
}

void print1(std::string const& s) {
    std::cout<<"n1 = "<<s<<std::endl;
}

void print2(std::string const& s) {
    std::cout<<"n2 = "<<s<<std::endl;
}

int main() {
    qi::rule<std::string::const_iterator, std::string()> number = +qi::digit;
    std::string input = "1+2";
    std::string result;
    if( test::parse(input, number[print1] >> *( qi::char_("+-") >> number[print2]) >> qi::eoi, result) ) {
        std::cout<<"Match! result = "<<result<<std::endl;
    } else {
        std::cout<<"Not match!"<<std::endl; 
    }
    return 0;
}

I'm expecting the output of this program is,

n1 = 1
n2 = 2
Match! result = 1+2

But the output is actually very strange for n2,

n1 = 1
n2 = 1+2
Match! result = 1+2

Why is the second number's attribute "1+2" instead of just "2"?

I know there are some other ways to parse this expression such as using qi::int_. I'm just wondering why do I get this strange attribute from it. Thanks!

1

There are 1 answers

1
sehe On BEST ANSWER

Backtracking doesn't undo changes to container attributes. Adjacent compatible parser expressions bind to the same container attribute.

Both attributes get bound to the same container attribute (result) which means you are printing the same variable twice.

If you didn't want that, be explicit, e.g.

Live On Coliru

std::string result;
std::vector<std::string> v;
if( test::parse(input, number[px::bind(print1, qi::_1)] >> *qi::as_string[qi::char_("+-") >> number[print2]] >> qi::eoi, result, v) ) {
    std::cout<<"Match! result = "<<result<<std::endl;
    for (auto s : v)
        std::cout << s << "\n";
} else {

Prints

n1 = 1
n2 = +2
Match! result = 1
+2

Now I don't know what you want to achieve, but this could be close:

if (test::parse(input, qi::raw [number[print_("n1",_1)] > *(qi::char_("+-") >> number[print_("n2",_1)]) ] >> qi::eoi, result)) {

Live On Coliru

#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix.hpp>

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

namespace test {
    template< typename Rng,typename Expr,typename... Attr >
    bool parse(Rng const& rng,Expr const& expr,Attr&... attr) noexcept {
        auto itBegin = boost::begin(rng);
        auto itEnd   = boost::end(rng);
        try {
            return qi::parse(itBegin,itEnd,expr,attr...);
        } catch(qi::expectation_failure<decltype(itBegin)> const&) {
            return false;
        }
    }
}

void printn(std::string const& label, std::string const& s) {
    std::cout << label << " = " << s << std::endl;
}

BOOST_PHOENIX_ADAPT_FUNCTION(void, print_, printn, 2)

int main() {
    qi::rule<std::string::const_iterator, std::string()> number = +qi::digit;
    std::string input = "1+2";
    std::string result;
    using qi::_1;

    if (test::parse(input, qi::raw [number[print_("n1",_1)] > *(qi::char_("+-") >> number[print_("n2",_1)]) ] >> qi::eoi, result)) {
        std::cout<<"Match! result = "<<result<<std::endl;
    } else {
        std::cout<<"Not match!"<<std::endl; 
    }
    return 0;
}

Prints

n1 = 1
n2 = 2
Match! result = 1+2

BONUS

A little more separation of concern:

Live On Coliru

void printn(int n, std::string const& s) {
    std::cout << "n" << n << " = " << s << std::endl;
}

BOOST_PHOENIX_ADAPT_FUNCTION(void, print_, printn, 2)

int main() {
    qi::rule<std::string::const_iterator, std::string()> number;

    int n = 1;
    number %= qi::as_string[+qi::digit] [print_(px::ref(n)++, qi::_1)];

    std::string input = "1+2";
    std::string result;

    if (test::parse(input, qi::raw [number > *(qi::char_("+-") >> number) ] >> qi::eoi, result)) {
        std::cout << "Match! result = " << result << std::endl;
    } else {
        std::cout << "Not match!" << std::endl;
    }
    return 0;
}