Search code examples
c++parsingboostboost-spiritboost-spirit-qi

Why doesn't this boost::spirit::qi rule successfully parse?


I have the following boost::spirit::qi parser rule:

namespace qi = boost::spirit::qi;
qi::rule<Iterator, BroadbandCurve(), Skipper> Cmd_BBNSET;


Cmd_BBNSET = +(qi::float_ >> qi::float_) >> qi::int_ >> qi::int_ >> lit("BBNSET");

I'm trying to get it to emit the following attribute:

struct FreqLevelPair
{
    float freq;
    float dbLevel;
};
BOOST_FUSION_ADAPT_STRUCT(
    FreqLevelPair,
    (float, freq)
    (float, dbLevel)
)

struct BroadbandCurve
{
    std::vector<FreqLevelPair> freqPairs;
    int numFreqPairs; //Ignored since we can just count the number of pairs outright...
    int curveNum; //ID number
};
BOOST_FUSION_ADAPT_STRUCT(
    BroadbandCurve,
    (std::vector<FreqLevelPair>, freqPairs)
    (int, numFreqPairs)
    (int, curveNum)
)

As you can see, I'm attempting to parse one or more pairs of floats, followed by two ints, followed by the literal "BBNSET." All of this code compiles, however when I attempt to parse a valid BBNSET command of the form:

0.0 80.0 50.0 25.0 100.0 10.0 3 0 BBNSET

the parse fails. I'm unable to determine why. I've attempted wrapping the float pairs in a lexeme directive, and changing the + to a *, but no matter what I've attempted, the command still fails to parse, despite compiling without a problem.

What am I doing wrong, and will this rule emit the attribute as expected once it is parsing correctly?


Solution

  • Sharth was really quick to note the cause.

    The solution, IMO is to use strict_real_policies

    See it Live On Coliru

    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    
    namespace qi = boost::spirit::qi;
    
    struct FreqLevelPair
    {
        float freq;
        float dbLevel;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(
        FreqLevelPair,
        (float, freq)
        (float, dbLevel)
    )
    
    struct BroadbandCurve
    {
        std::vector<FreqLevelPair> freqPairs;
        int numFreqPairs; //Ignored since we can just count the number of pairs outright...
        int curveNum; //ID number
    };
    
    BOOST_FUSION_ADAPT_STRUCT(
        BroadbandCurve,
        (std::vector<FreqLevelPair>, freqPairs)
        (int, numFreqPairs)
        (int, curveNum)
    )
    
    int main()
    {
        typedef std::string::const_iterator Iterator;
        typedef qi::space_type Skipper;
    
        qi::real_parser<double, qi::strict_real_policies<double> > strict_real_;
    
        qi::rule<Iterator, BroadbandCurve(), Skipper> Cmd_BBNSET;
        Cmd_BBNSET = +(strict_real_ >> strict_real_) >> qi::int_ >> qi::int_ >> qi::lit("BBNSET");
    
        std::string const input("0.0 80.0 50.0 25.0 100.0 10.0 3 0 BBNSET");
        auto f(input.begin()), l(input.end());
        bool ok = qi::phrase_parse(f, l, Cmd_BBNSET, qi::space);
    
        if (ok)
        {
            std::cout << "Parse succeeded\n";
        } else
        {
            std::cout << "Parse failed\n";
        }
    
        if (f!=l)
            std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
    }