Search code examples
boost-spirit

Boost custom parser failed to work with qi::parse


I just adapted an example parser in the spirit code base and tested that it works. The code is as below,

#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_assign_actor.hpp>
#include <boost/spirit/home/classic/core/parser.hpp>
//#include <boost/spirit/include/qi.hpp>  //UNCOMMENT THIS!
#include <iostream>
#include <string>
#include <cstdint>

///////////////////////////////////////////////////////////////////////////////
using namespace std;
using namespace BOOST_SPIRIT_CLASSIC_NS;

//namespace qi = boost::spirit::qi; //UNCOMMENT THIS!


template <typename T, int DecimalPlaces=4>
struct fixed_point_parser : boost::spirit::classic::parser<fixed_point_parser<T>>
{
    static constexpr int pow10[10] = {
        1, 10, 100, 1000, 10000,
        100000, 1000000, 10000000, 100000000, 1000000000
    };

    typedef fixed_point_parser<T> self_t;

    template <typename ScannerT>
    struct result
    {
        typedef typename match_result<ScannerT, uint64_t>::type type;
    };

    fixed_point_parser() {}  //ctor

    template <typename ScannerT>
    typename parser_result<self_t, ScannerT>::type
        parse(ScannerT const& scan) const
    {
        typedef typename parser_result<fixed_point_parser, ScannerT>::type RT;

        if (!scan.at_end())
        {
            uint_parser<int, 10, 1, 6> int_part;
            typename ScannerT::iterator_t save = scan.first;

            RT n_match = int_part.parse(scan);
            T  n = n_match.has_valid_attribute() ?
                n_match.value() : T(0);
            bool got_a_number = n_match;

            if (got_a_number)
            {
                n *= pow10[DecimalPlaces];
                if (ch_p(".").parse(scan))
                {
                    RT frac = int_part.parse(scan);
                    if (frac)
                    {
                        n += frac.value() * pow10[DecimalPlaces - frac.length()];
                        return scan.create_match(n_match.length() + frac.length() + 1, n, save, scan.first);
                    }
                    else
                        return scan.no_match();
                }
                return scan.create_match(n_match.length(), n, save, scan.first);
            }

        }
        return scan.no_match();
    }
};


int main()
{

    string str = "1234.5";

    fixed_point_parser<uint64_t> f{};

//  if (qi::parse(str.begin(), str.end(), f)) //UNCOMMENT THIS!
    if (parse(str.begin(), str.end(), f).full) //COMMENT THIS OUT!
    {
        cout << "Parsing succeeded\n";
    }
    else
    {
        cout << "Parsing failed\n";
    }


    return 0;
}

The code will compile and parse correctly.

But once you uncomment out the lines with "UNCOMMENT THIS!" and try to make it work with qi, then compile would fail.

Can someone help with explaining why?


Solution

  • Ok i actually figured out how to do it.

    template <typename T, int DecimalPlaces=4 >
    struct fixed_point_parser
        : primitive_parser<fixed_point_parser<T> >
    {
        static constexpr int pow10[10] = {
            1, 10, 100, 1000, 10000,
            100000, 1000000, 10000000, 100000000, 1000000000
        };
    
    
        template <typename Context, typename Iterator>
        struct attribute
        {
            typedef T type;
        };
    
        template <typename Iterator, typename Context, typename Skipper>
        bool parse(Iterator& first, Iterator const& last
            , Context& /*context*/, Skipper const& skipper
            , T& attr_) const
        {
            bool got_a_number = extract_uint<T, 10, 1, -1>::call(first, last, attr_);
    
            if (got_a_number)
            {
                attr_ *= pow10[DecimalPlaces];
                if (ureal_policies<T>::parse_dot(first, last))
                {
                    T frac_part;
                    Iterator save = first;
                    bool got_frac_part = extract_uint<T, 10, 1, -1>::call(first, last, frac_part);
                    if (got_frac_part)
                    {
                        auto dist = first - save;
                        attr_ += frac_part * pow10[DecimalPlaces - dist];
                    }
                }
                return true;
            }
            return false;
        }
    
        template <typename Iterator, typename Context
            , typename Skipper, typename Attribute>
            bool parse(Iterator& first, Iterator const& last
                , Context& context, Skipper const& skipper
                , Attribute& attr_param) const
        {
            // this case is called when Attribute is not T
            T attr_;
            if (parse(first, last, context, skipper, attr_))
            {
    
                boost::spirit::traits::assign_to(attr_, attr_param);
                return true;
            }
            return false;
        }
    
        template <typename Context>
        info what(Context& /*context*/) const
        {
            return info("fixed_point");
        }
    };