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

How to use the qi::hold[] Parser directive. (Issue with attribute type for boost::swap)


I have a parser that parses into a boost::variant<int, double, std::string>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using namespace std;

typedef map<string,  boost::variant<int, double, string> > namevalue;
typedef pair<string, boost::variant<int, double, string> > namevaluepair;

template <typename Iterator>
struct keys_and_values2
    :   grammar<Iterator, namevalue(), ascii::blank_type>
{
    keys_and_values2()
        :   keys_and_values2::base_type(start)
    {
        start %= query >> qi::eoi;
        query =  +pair;
        value =  qi::int_ | qi::double_ | qi::lexeme[+(qi::char_ - qi::eol)] ;
        pair  =  key >> qi::lit(':') >> value >> qi::eol;
        key   =  qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
    }
    qi::rule<Iterator, namevalue(), ascii::blank_type> start;
    qi::rule<Iterator, namevalue(), ascii::blank_type> query;
    qi::rule<Iterator, namevaluepair(), ascii::blank_type> pair;
    qi::rule<Iterator, std::string(), ascii::blank_type> key;
    qi::rule<Iterator, boost::variant<int, double, string>(), ascii::blank_type> value;
};

the input to parse is here:

Sarai    :    52.731199473801
Jamiya   :    Jelly Drop
Clara    :    -92.408605869885
Janelle  :    132349223
Briley   :    -40.905352495602

This fails on the first line "Sarai". If I reverse the parsing of double and int like this:

value =  qi::double_ | qi::int_ | qi::lexeme[+(qi::char_ - qi::eol)] ;

It works fine, but the integer value of "Janelle" is parsed as double. Following the BOOST FAQ and the BOOST documentation, I would like to use qi::hold[] like this:

value =  qi::hold[qi::int_] | qi::hold[qi::double_] | qi::lexeme[+(qi::char_ - qi::eol)] ;

But with this, I get a message about missing function: swap(). This is actually documented in the boost docs (note). But the explanation is very terse. I am unable to find the correct attribute type for this swap function. Can anybody help here?

Compiler message here:

E:\Boost\boost_1_58_0\boost/spirit/home/support/attributes.hpp(1036) : error C2784: 'void boost::spirit::swap(boost::spirit::multi_pass<T,Policies> &,boost::spirit::multi_pass<T,Policies> &)' : could not deduce template argument for 'boost::spirit::multi_pass<T,Policies> &' from 'int' 


Update
The answer of sehe works with the data above. However If I change the input to the following:

Sarai    :    52.731199473801
Jamiya   :    Jelly Drop
Clara    :    -92.408605869885
Rebekah  :    240ad9beb53bbfafcd5
Janelle  :    132349223
Cloe     :    456ABCabvc
Briley   :    -40.905352495602

I'll have a problem with the "Rebekah" value. I also see that the order in which the values are evaluated is important. This time, the issue is not with the double value anymore, it is between the int and the string. I would like to implement something like this: (policy?)

  1. everything that has only digits, an optional minus sign and contains a dot is a double
  2. everything that has only digits and an optional minus sign is int
  3. everything else is std::string


Solution
Sometimes, it is more important to understand the question than the problem.

The issue was not the parser policy, it was my definition of the parsing rules (see above). Rule 3 "everything else" actually includes: "everything up to EOL". Therefore all three alternatives in the rule for value have to match this:

value =   strict_double >> qi::eol
        | qi::int_ >> qi::eol
        | qi::lexeme[+(qi::char_ - qi::eol)] >> qi::eol;

With this change, sehe's answer just works like a charm!


Solution

  • Hold is not used for this.

    Think about it: how would it help? It won't make the integer branch not match, so it will still fail to parse, and hold doesn't revert anything.

    hold[] is mainly useful for container attributes¹, where partial parses could have modified the attribute. Containers usually already have swap implemented. You were barking up the wrong tree here

    ¹ including strings, see e.g.
    Understanding Boost.spirit's string parser
    boost::spirit::qi duplicate parsing on the output
    Boost spirit revert parsing
    Boost Spirit optional parser and backtracking


    Solving the problem:

    You can use the strict real policy to parse only real values as double.

    value =  strict_double | qi::int_ | qi::lexeme[+(qi::char_ - qi::eol)] ;
    
    // with
    qi::real_parser<double, qi::strict_real_policies<double> > strict_double;
    

    See also Parse int or double using boost spirit (longest_d) for some unit tests

    Live On Coliru

    #define BOOST_SPIRIT_DEBUG
    #include <boost/fusion/adapted/std_pair.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <map>
    
    namespace qi    = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    
    typedef boost::variant<int, double, std::string> value_t;
    typedef std::map<std::string, value_t> namevalue;
    typedef std::pair<std::string, value_t> namevaluepair;
    
    template <typename Iterator>
    struct keys_and_values2
        :   qi::grammar<Iterator, namevalue(), ascii::blank_type>
    {
        keys_and_values2() : keys_and_values2::base_type(start)
        {
            start %= query >> qi::eoi;
            query =  +pair;
            value =  strict_double | qi::int_ | qi::lexeme[+(qi::char_ - qi::eol)] ;
            pair  =  key >> qi::lit(':') >> value >> qi::eol;
            key   =  qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
    
            BOOST_SPIRIT_DEBUG_NODES((start)(query)(value)(pair)(key))
        }
      private:
        qi::real_parser<double, qi::strict_real_policies<double> > strict_double;
        qi::rule<Iterator, namevalue(),     ascii::blank_type> start;
        qi::rule<Iterator, namevalue(),     ascii::blank_type> query;
        qi::rule<Iterator, namevaluepair(), ascii::blank_type> pair;
        qi::rule<Iterator, std::string(),   ascii::blank_type> key;
        qi::rule<Iterator, value_t(),       ascii::blank_type> value;
    };
    
    int main() {
        typedef boost::spirit::istream_iterator It;
        It f(std::cin >> std::noskipws), l;
    
        keys_and_values2<It> g;
        namevalue data;
        bool ok = qi::phrase_parse(f,l,g,ascii::blank,data);
    
        if (ok) {
            std::cout << "Parse succeeded:\n";
            for(auto& p : data)
                std::cout << "\t'" << p.first << "'\t-> " << p.second << "\n";
        } else
            std::cout << "Parse failed\n";
    
        if (f!=l)
            std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
    

    Prints

    Parse succeeded:
        'Briley'    -> -40.9054
        'Clara' -> -92.4086
        'Jamiya'    -> Jelly Drop
        'Janelle'   -> 132349223
        'Sarai' -> 52.7312
    

    And the debug info (if enabled)

    <start>
    <try>Sarai    :    52.731</try>
    <query>
        <try>Sarai    :    52.731</try>
        <pair>
        <try>Sarai    :    52.731</try>
        <key>
            <try>Sarai    :    52.731</try>
            <success>:    52.731199473801</success>
            <attributes>[[S, a, r, a, i]]</attributes>
        </key>
        <value>
            <try>    52.731199473801\n</try>
            <success>\nJamiya   :    Jelly</success>
            <attributes>[52.7312]</attributes>
        </value>
        <success>Jamiya   :    Jelly </success>
        <attributes>[[[S, a, r, a, i], 52.7312]]</attributes>
        </pair>
        <pair>
        <try>Jamiya   :    Jelly </try>
        <key>
            <try>Jamiya   :    Jelly </try>
            <success>:    Jelly Drop\nClar</success>
            <attributes>[[J, a, m, i, y, a]]</attributes>
        </key>
        <value>
            <try>    Jelly Drop\nClara</try>
            <success>\nClara    :    -92.4</success>
            <attributes>[[J, e, l, l, y,  , D, r, o, p]]</attributes>
        </value>
        <success>Clara    :    -92.40</success>
        <attributes>[[[J, a, m, i, y, a], [J, e, l, l, y,  , D, r, o, p]]]</attributes>
        </pair>
        <pair>
        <try>Clara    :    -92.40</try>
        <key>
            <try>Clara    :    -92.40</try>
            <success>:    -92.40860586988</success>
            <attributes>[[C, l, a, r, a]]</attributes>
        </key>
        <value>
            <try>    -92.408605869885</try>
            <success>\nJanelle  :    13234</success>
            <attributes>[-92.4086]</attributes>
        </value>
        <success>Janelle  :    132349</success>
        <attributes>[[[C, l, a, r, a], -92.4086]]</attributes>
        </pair>
        <pair>
        <try>Janelle  :    132349</try>
        <key>
            <try>Janelle  :    132349</try>
            <success>:    132349223\nBrile</success>
            <attributes>[[J, a, n, e, l, l, e]]</attributes>
        </key>
        <value>
            <try>    132349223\nBriley</try>
            <success>\nBriley   :    -40.9</success>
            <attributes>[132349223]</attributes>
        </value>
        <success>Briley   :    -40.90</success>
        <attributes>[[[J, a, n, e, l, l, e], 132349223]]</attributes>
        </pair>
        <pair>
        <try>Briley   :    -40.90</try>
        <key>
            <try>Briley   :    -40.90</try>
            <success>:    -40.90535249560</success>
            <attributes>[[B, r, i, l, e, y]]</attributes>
        </key>
        <value>
            <try>    -40.905352495602</try>
            <success>\n</success>
            <attributes>[-40.9054]</attributes>
        </value>
        <success></success>
        <attributes>[[[B, r, i, l, e, y], -40.9054]]</attributes>
        </pair>
        <pair>
        <try></try>
        <key>
            <try></try>
            <fail/>
        </key>
        <fail/>
        </pair>
        <success></success>
        <attributes>[[[[B, r, i, l, e, y], -40.9054], [[C, l, a, r, a], -92.4086], [[J, a, m, i, y, a], [J, e, l, l, y,  , D, r, o, p]], [[J, a, n, e, l, l, e], 132349223], [[S, a, r, a, i], 52.7312]]]</attributes>
    </query>
    <success></success>
    <attributes>[[[[B, r, i, l, e, y], -40.9054], [[C, l, a, r, a], -92.4086], [[J, a, m, i, y, a], [J, e, l, l, y,  , D, r, o, p]], [[J, a, n, e, l, l, e], 132349223], [[S, a, r, a, i], 52.7312]]]</attributes>
    </start>