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

Save token position


I need to save parsed token position along with token. This is simplified sample code.

#include <boost/config/warning_disable.hpp>
#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_stl.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <iostream>
#include <string>
#include <vector>

namespace client
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phoenix = boost::phoenix;

    struct double_w_pos
    {
        double_w_pos() : d(-1.0), pos(0) {}
        double_w_pos(double d, size_t p) : d(d), pos(p) {}
        double d;
        size_t pos;
    };

    template <typename Iterator>
    bool parse_numbers(Iterator first, Iterator last, std::vector<double_w_pos>& v)
    {
        using qi::double_;
        using qi::phrase_parse;
        using qi::_1;
        using ascii::space;
        using phoenix::at_c;
        using phoenix::push_back;

        bool r = phrase_parse(first, last,
            (
                double_[push_back(phoenix::ref(v), phoenix::construct<double_w_pos>(qi::_1, 0))] % ','
            )
            ,
            space);

        if (first != last)
            return false;
        return r;
    }
}

int main()
{
    std::vector<client::double_w_pos> v;
    std::string str = "2.0, 3.45, 5.67, 2.08";

    client::parse_numbers(str.begin(), str.end(), v);

    for(auto i : v)
        std::cout << i.d << " at " << i.pos << ", ";
    std::cout << std::endl;

    return 0;
}

You can run it live on Coliru Now 0 is saved as token position. How can i get actual parsing position. It need not to be size_t. It can be iterator or anything, what can be used to identify position in text, at which token was found. Thanks.

UPDATE I didn't manage to get position as size_t directly from parsing, but i managed to get iterator position. It is not exactly what i need, but it is better than nothing. Here is the code

#include <boost/config/warning_disable.hpp>
#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_stl.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>

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

namespace client
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phoenix = boost::phoenix;

    struct double_w_pos
    {
        double_w_pos(double d, std::string::iterator p) : d(d), pos(p) {}
        double d;
        std::string::iterator pos;
    };


    template <typename Iterator>
    bool parse_numbers(Iterator first, Iterator last, std::vector<double_w_pos>& v)
    {
        using qi::double_;
        using qi::phrase_parse;
        using qi::_1;
        using ascii::space;
        using phoenix::at_c;
        using phoenix::push_back;
        using phoenix::begin;
        using boost::spirit::repository::qi::iter_pos;

        bool r = phrase_parse(first, last,
            (
               (iter_pos >> qi::double_)[push_back(phoenix::ref(v), phoenix::construct<double_w_pos>(qi::_2,  qi::_1))] % ","
            )
            ,
            space);

        if (first != last)
            return false;
        return r;
    }
}

int main()
{
    std::vector<client::double_w_pos> v;
    std::string str = "2.0, 3.45, 5.67, 2.08";

    client::parse_numbers(str.begin(), str.end(), v);

    for(auto i : v)
        std::cout << i.d << " at " << std::distance(str.begin(), i.pos) << ", ";
    std::cout << std::endl;

    return 0;
}

Coliru link


Solution

  • You can follow the following guides:

    But by far my preferred method (least intrusive) uses on_success which is, outside of a sample that uses it, sadly under-documented: